JVM学习笔记(一、Class文件结构)

问题:

1、是不是只有Java编译器才能完成Java到class字节码文件的编译过程?

不是,像Java/JRuby/Groovy等程序都可以通过自己的编译器转成字节码(class)文件,然后交给JVM。

2、什么是u1、u2、u4、u8。

  • u1:一个子字节。
  • u2:两个子字节。
  • u4:四个子字节。
  • u8:八个子字节。

Class文件组成内容:

虚拟机指令、符号表、其它复制信息(这三个都由两种数据结构组成,无符号数、表)。

无符号数:数值。

表:xxx_info。

Class文件格式:

u4 u2 u2 u2 cp_info u2 u2 u2 u2 u2 u2 field_info u2 method_info u2 attribute_info

解释:

1、魔数:

每个Class文件的头四个字节数为魔数,唯一作用是确定这个文件是否是能被虚拟机接受的Class文件

目前版本的值都是固定的,0xCAFEBABE(咖啡宝贝)。

2、紧接着魔数后面的4个字节就是Class文件的版本号:5-6字节,次版本号;7-8字节主版本号。

3、Class文件版本号后面紧随的是常量池入口。

常量池主要分为两大类:

  • 字面量:与Java中的常量概念类似;int a = 3,字面量就是等号右边的东西
  • 符号引用:符号引用包含三类常量。
    • 类和接口的全限名称:org.springframework.xxxBean。
    • 字段名称和描述符:private/protected/public。
    • 方法的名称和描述符:private/protected/public。

常量池:

常量池结构表:

访问标志:

分析示例

public class Test extends Thread implements Serializable {

    public int count(int sum) {
        int i = sum = 1;
        return i;
    }
}

以上代码生成class文件用二进制格式打开后如下(部分):

前置说明,address 00000000记为00,00000010记为10

1、[00, 0]至[00, 7]如上所述为魔数和版本号,[00, 8]至[00, 9]为常量池计数器,换算下来为22,但因为常量池数量为常量池计数器-1,所以常量池的数量是21。

2、然后我们来看看常量池的数据:

从[00, a]开始,首先0a的10进制为10,对照上表是CONSTANT_Methodref_info

我们将其列为:

tag   = 10
index = 00 03 >>> 00 03
index = 13 07 >>> 19 07

以此类推,[00, f]:class_info

tag   = 07
index = 00 14 >>> 00 20

[00, f]:class_info

tag   = 07
index = 00 15 >>> 00 21

3、但其实我们不用一步步的分析,可以直接通过javap -v Test.class命令就可以了。

根据构造函数中invokespecial #1的入口分析后得出如下:

第一步:#1,然后我们找到常量池的#1,其中会先到#3再到#19。

第二步:#3,转到#21。

第三步:#19,转到#5,#6,以此类推。

其中我们可以看到<init>,这是对象实例的方法;如果是<clinit>则是类和接口初始化,如静态代码块的加载。

4、接下来就是访问标志、类索引、父类索引、接口计数器等等,它们都对应两个字节。

  • 访问标志:[110, 5],[110, 6]两个字节,也就是00 21。因访问标志为public所以对应标志为0x0001,计算公式 >>> 访问标志1 | 访问标志2 | 访问标志n | ACC_SUPER的值。
    • public class >>> 0x0001 | 0x0020 >>> 0x0021,所以对应了00 21。
    • public abstract class >>> 0x0001 | 0x0400 | 0x0020 >>> 0x0421,你再重新编译便会发现值为04 21。
    • 以此类推,只要对照访问标志的字典表就好了。
  • 类索引:[110, 7],[110, 8] >>> 00 02,对应常量表#2,com/jdr/maven/rabbitmq/helloworld/Test。
  • 父类索引:[110, 9],[110, a] >>> 00 03,对应常量表#3,java/lang/Thread。
  • 接口计数器:[110, b],[110, c] >>> 00 01,也就是一个接口。
  • 内容太多就不一一分析了,只需按图索骥便可。
原文地址:https://www.cnblogs.com/bzfsdr/p/12097626.html