添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

前言

前段时间在看算法这块的东西,看的我是头昏脑胀。所以这几天又捡起了 《深入理解Java虚拟机》 这本书,这次主要看的是书中的 第三部分 ,我一直崇尚知识是在不断的总结和不断地学习相互交叉,这样才能学以致用,用而有据。

概念

字节码

字节码指的是 Java 中的 .java 文件经过编译( javac )后生成的固定格式文件 .class 文件以供 JVM 使用。
之所以被称为字节码文件是因为字节码文件是由十六进制值组成, JVM 以两个十六进制值为一组,即一个字节进行读取。同时 JVM 也针对不同操作系统和平台进行优化,这也就是 Java 号称 一次编译,到处运行 的根本原因。

由此又可以引出一个问题,由于 JVM 规范的存在,那么只要我们最终可以生成符合 JVM 规范的字节码文件那就可以在 JVM 上运行了,这也就产生了其他运行在 JVM 上的语言(如 Scale、Kotlin、Groovy ),可以通过其他语言可以扩展 Java 所没有的特性和语法糖。

字节码增强

字节码增强指的是在 Java 字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。字节码增强的应用场景主要是减少冗余代码,对开发人员屏蔽底层的实现细节。

  • 通过创建原始类的一个子类,也就是动态创建这个类继承原有的类,从而扩展原有类的方法。
  • 直接修改原有类生成的 Class 文件,在许多类的跟踪过程中都会用到(运行时修改、类加载时修改字节码信息)。
  • 基础

    字节码格式

    一个 .java 文件经过编译( javac )后就会生成 .class 文件。

    如下所示,左边为原始代码,右边为编译后的字节码:
    de80a67bdb1c0a47d6adb3a0420a826d1235757.png

    字节码文件解析:

  • 魔数:每个 Class 文件头 4 个字节代表魔数,它代表了这个文件是否是一个能被虚拟机接受的 Class 文件。魔数固定值为: CAFEBABE
    有趣的是,魔数的固定值是 Java 之父 James Gosling 制定的,为 CafeBabe (咖啡宝贝),而 Java 的图标为一杯咖啡。

  • 版本号:前两个字节代表次版本号 minorversion ,后两个字节代表主版本号 majorversion 。将四个字节的十六进制值转换为十进制就是对应的版本号。

  • 常量池:常量池的大小是不固定的,会根据类中常量的多少来确定。其中首选由一个 2 个字节十六进制的数来定义常量池长度,计算出常量池的十进制是多少,然后减一得出常量池的数量。
    常量池的类型:
    | 常量 | 类型 | 描述 |

    |———————————-|—————|———————————–|
    | CONSTANT_Utf8_info | tag标志位为1 | UTF-8编码的字符串 |
    | CONSTANT_Integer_info | tag标志位为3 | 整形字面量 |
    | CONSTANT_Float_info | tag标志位为4 | 浮点型字面量 |
    | CONSTANT_Long_info | tag标志位为5 | 长整形字面量 |
    | CONSTANT_Double_info | tag标志位为6 | 双精度字面量 |
    | CONSTANT_Class_info | tag标志位为7 | 类或接口的符号引用 |
    | CONSTANT_String_info | tag标志位为8 | 字符串类型的字面量 |
    | CONSTANT_Fieldref_info | tag标志位为9 | 字段的符号引用 |
    | CONSTANT_Methodref_info | tag标志位为10 | 类中方法的符号引用 |
    | CONSTANT_InterfaceMethodref_info | tag标志位为11 | 接口中方法的符号引用 |
    | CONSTANT_NameAndType_info tag | 标志位为12 | 字段和方法的名称以及类型的符号引用 |
    | CONSTANT_Method-Handle_info | tag标志位为15 | 方法句柄 |
    | CONSTANT_Method-Type_info | tag标志位为16 | 方法类型 |
    | CONSTANT_Invoke-Dynamic_info | tag标志位为18 | 动态方法调用点 |

    常量池分布:
    ac90457d635b90e2c08bf7659b0b7dfd50229.png

  • 访问标志:常量池结束之后的两个字节,描述该 Class 是类还是接口,以及是否被 Public、Abstract、Final 等修饰符修饰。
    访问标志的类型:
    | 标志名称 | 标志值 | 含义 |
  • |———————-|———–|——————————-|
    | ACC_PUBLIC | 0X0001 | public 类型 |
    | ACC_PRIVATE | 0X0002 | private 类型 |
    | ACC_FINAL | 0X0010 | 声明为 final ,只有类可以设置 |
    | ACC_SUPER | 0X0020 | 使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK1.0.2发生过改变,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类都为真 |
    | ACC_INTERFACE | 0X0200 | 接口 |
    | ACC_ABSTRACT | 0X0400 | abstract 类型,对于接口或者抽象类来说,此标志值为真,其他类为假 |
    | ACC_SYNTHETIC | 0X1000 | 这个类并非由用户代码产生 |
    | ACC_ANNOTATION | 0X2000 | 注解 |
    | ACC_ENUM | 0X4000 | 枚举 |

  • 当前类索引:访问标志后的两个字节,描述的是当前类的全限定名。这两个字节保存的值为常量池中的索引值,根据索引值就能在常量池中找到这个类的全限定名。
  • 父类索引:当前类名后的两个字节,描述父类的全限定名,同上,保存的也是常量池中的索引值。
  • 接口索引:父类名称后为两字节的接口计数器,描述了该类或父类实现的接口数量。紧接着的 N 个字节是所有接口名称的字符串常量的索引值。
  • 字段表:字段表用于描述类和接口中声明的变量,包含类级别的变量以及实例变量,但是不包含方法内部声明的局部变量。
    0f795d2b2b28ce96b5963efb2e564e5a197874.png
  • 方法表:字段表结束后为方法表,方法表也是由两部分组成,第一部分为两个字节描述方法的个数;第二部分为每个方法的详细信息。
    0f795d2b2b28ce96b5963efb2e564e5a197874.png
  • 属性表:字节码的最后一部分,该项存放了在该文件中类或接口所定义属性的基本信息。
  • 字节码文件中格式释义:

  • u2、u4 分别代表有两个字节、四个字节。
  • Class 类文件的伪结构中只有两种数据类型:无符号数(unsigned quantity)和表(table)。
    无符号数属于基本的数据类型, u2、u4 分别代表两个字节和四个字节的无符号数。而其余的 cp_info、field_info、method_info、attribute_info 就是表。
  • 字节码常用工具