深入理解JVM--字节码文件结构解析

深入理解JVM--字节码文件结构解析

  1. 在cmd窗口使用javap -verbose 类名称 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息
  2. 字节码整体结构
    深入理解JVM--字节码文件结构解析_第1张图片
  3. 魔数:所有的.class字节码文件的前4个字节都是魔数,魔数为固定值:0xCAFEBABE,若魔数不对JVM会认为该字节码文件非法而不去加载
  4. 魔数之后的4个字节为版本信息,前两个字节表示minor version(次版本号),后两个字节表示major version(主版本号)。 如:00 00 00 34,换算成十进制,表示次版本号为0,主版本号为52.所以,该文件的版本号为:1.8.0。可以通过java -version命令来验证这一点。
  5. 常量池(constant pool): Class文件的资源仓库,Java类中定义的方法与变量信息,都是存储在常量池中。 常量池中主要存储两类常量:字面量和符号引用。 字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等。
    • 常量池数量: 常量池数量紧跟在主版本号后面,占据2个字节。
    • 常量池数组: 常量池数组则紧跟在常量池数量之后。 每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。 常量池数组中元素的个数=常量池数-1,根本原因在于:索引为0也是一个常量(保留常量),只不过他不位于常量表中,这个常量就对应null值; 所以,常量池的索引从1而非0开始。
    • 在JVM规范中,每个变量/字段都有描述信息,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,对象类型则使用字符L加对象的全限定名称来表示。 为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母来表示,如下所示:B - byte,C -char,D - double,F - float,I - int,J - long,S - short,Z - boolean,V - void,L - 对象类型,如Ljava/lang/String;
    • 对于数组类型来说,每一个维度使用一个前置的 [ 来表示,如 int[ ]被记录为 [ I;String[ ][ ]被记录为 [ [ Ljava/lang/String
    • 用描述符描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之内,如方法:String getName(int id,String name)的描述符为:(I,Ljava/lang/String;) Ljava/lang/String;
    • 常量池中的数据结构深入理解JVM--字节码文件结构解析_第2张图片
  6. 类的访问控制权限(占据2个字节)
  7. 类名(占据2个字节)
  8. 父类名(占据2个字节)
  9. 接口信息
    • 接口个数(占据2个字节)
    • 接口名(一个名字占据2个字节)
  10. 类中属性的信息
    • 属性的个数(占据2个字节)
    • 属性表深入理解JVM--字节码文件结构解析_第3张图片
  11. 类中方法的信息
    • 方法的个数(占据2个字节)
    • 方法表深入理解JVM--字节码文件结构解析_第4张图片
      • 方法的访问控制权限
      • 方法的名称的索引(占据2个字节,在常量池中)
      • 方法的描述索引(占据2个字节,在常量池中)
      • 方法中的属性个数(占据2个字节)
      • 方法中的属性表(一般有且只有一个,名称为Code)深入理解JVM--字节码文件结构解析_第5张图片
        • 属性名称(占据2个字节)
        • 属性所占字节码长度(占据4个字节)
        • max_stack: 该方法中栈的最大深度
        • max_locals: 该方法中所存在的属性
        • arg_size:(并不在字节码文件中,JVM自动判断生成) 该方法中的参数个数(大于等于1,默认会传入this)
        • 方法中真正执行代码 (助记符) 所占的长度
        • 助记符的信息(占据一个字节)
        • 方法中的额外属性表(一般有2个,LineNumberTableLocalVariableTable
          • LocalNumberTable(用于记录助记符与源码行号之间的关系)深入理解JVM--字节码文件结构解析_第6张图片
          • LocalVariableTable(用于记录方法中变量的具体信息)
            • LocalVariableTable所占字节码长度(4个字节)
            • LocalVariableTable所显示的属性个数(方法中属性个数)
            • 属性信息(占据10个字节)深入理解JVM--字节码文件结构解析_第7张图片
  12. 类中附加属性的个数(占据2个字节)
  13. 附加属性表
  14. 补充:
    • 很多人认为在类中定义的非静态变量是就地完成赋值的,其实不是。 众所周知,如果我们在类中没有定义构造方法,JVM会自动生成默认的构造方法,实际上分析Code中的字节码,我们可以得到非静态属性变量的赋值其实是在构造方法<.init>中通过执行码完成的。 如果有类中多个构造方法,那么所有的构造方法都会有对非静态属性变量赋值的执行码,即无论执行哪个构造方法,都会在构造方法中给类中的非静态变量赋值
    • 但是静态变量不同,同样分析Code中的字节码可以知道,静态变量的赋值与静态代码块的执行都是在JVM自动生成的<.clinit >(我们不可见)方法中完成
    • 比如以下案例:str与x的实际赋值其实是在JVM自动生成的构造方法中完成,而不是在定义处完成的,而 如果有多个构造方法,则相同的赋值执行码会在所有构造方法中存在;但是static静态块的执行和in变量的赋值则一定是在JVM自动生成的<.clinit>方法中完成
    • 深入理解JVM--字节码文件结构解析_第8张图片
  15. 博主自己写的小程序,可以实现对一些简单类的反编译,仅供参考学习哦O(∩_∩)O
    • https://pan.baidu.com/s/1BRWC0ZMJNu3RRtuuaaHfKA
    • nkqa

你可能感兴趣的:(JVM)