这一次,彻底弄懂 Java 字节码文件( 三 )


JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型 。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定情况下需要表达「不引用任何一个常量池」的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量表中,这个常量就对应值;所以,常量池的索引从1而非0开始 。
 
4.3.2 常量池项目类型Class文件结构中常量池中实际是有14种数据类型的,12~14种数据类型是在JDK1.7之后添加进来的(新增三种类型分别为:CONSTANT_MethodHandle_info、CONSTANT_MethodType_info、CONSTANT_InvokeDynamic_info),主要是为了更好的支持动态语言调用的 。但是,最常用如下所列的列出了11种常规的数据类型:

这一次,彻底弄懂 Java 字节码文件

文章插图
上述常量都是以「CONSTANT」开头,以「info」结尾的常量名 。每一个常量包含的信息的段都是不同的,我们可以根据每一个段自身的起始和结束位置是什么来进行分析 。
抽出两个代表性的常量进行解析:
CONSTANT_Utf8_info
如果这个tag的值为1,占1个字节,它就表示的UTF-8编码的字符串;length,占2个字节,比如length值是4,表示的是从length的下后面读取4个字节长度的字符串 。
这个就表示CONSTANT_Utf8_info的具体的文本内容 。就是说根据length就能够知道接下来我要读取多少个字节才能读完,这些字节是由bytes来表示的 。
CONSTANT_Fieldref_info
tag是U1类型,值为9 。有两个index值,都是U2类型的,第一个index代表的是指向声明字段的类或接口描述符CONSTANT_Class_info的索引项,第二个index代表的指向字段描述符CONSTANT_NameAndType_info的索引项 。
具体可以理解为当我们定义一个字段时,一定是附属在某一个类上的,所以要先索引到类信息上,可以具体看下CONSTANT_Class_info,其tag是U1类型,值为7,它的index代表指向全限定名常量项的索引,很好理解了 。
然后再找到这个字段的描述符,这里指向了会索引到CONSTANT_NameAndType_info,其tag是U1类型,值为12,根据两个index的描述可以理解为要有字段或方法的名称以及字段或方法的描述符即可找到源码中对应的字段和方法 。
 
4.3.3 常量池结构分析接下来,我们以上述Java字节码结构总表为依据分析下Java字节码十六进制对应到Java字节码文件中的constant_pool常量池 。
Java字节码十六进制:
从第9位开始的十六进制
0A 00 04 00 14 0A表示值为10,从字节码结构总表中找到值为10的是CONSTANT_Methodref_info,有两个index值,第一个index占用的字节 00 04 转换为十进制为4,第二个index占用的字节00 14 转化为十进制为20 。
从Java字节码文件中Constant pool定义可看到:
 Constant pool: # #1 = Methodref #4.#20 // java/lang/Object."<init>":V索引到位置#4和#20,从常量池中找到这两个索引项如下:
 #4 = Class #23 // java/lang/Object#20 = NameAndType #7:#8 // "<init>":V这两个索引正好可以跟结构总表中对应上 。其中,#4表示的类全限定名为java/lang/Object,而索引20位置又引用了#7:#8 。继续找到#7和#8:
 #7 = Utf8 <init>#8 = Utf8 V从第16位开始的十六进制
09 00 03 00 15 这个标志位值为09,从字节码结构总表中找到值为9的常量为CONSTANT_Fieldref_info,其后面跟着两个index,对应十六进制转换为十进制为3和21 。
  #2 = Fieldref #3.#21 // com/dskj/jvm/bytecode/MyTest1.a:I对应有两个索引项#3和#21,如下所示:
 #3 = Class #22 // com/dskj/jvm/bytecode/MyTest1#21 = NameAndType #5:#6 // a:I索引项#3引用了索引项#22,索引项#21引用了索引项#5:#6
 #22 = Utf8 com/dskj/jvm/bytecode/MyTest1#5 = Utf8 a#6 = Utf8 I根据以上,#5表示的变量名为a,#6表示的变量a的返回类型是I,即int类型的 。也就知道了#2 = Fileldref,对应的是com/dskj/jvm/bytecode/MyTest1.a:I 。
对应到MyTest1类的变量:
 private int a = 1;从第21位开始的十六进制
07 00 16 标志位为07,值为7字节码结构总表中对应常量CONSTANT_Class_info,索引占用2个字节,对应转换为十进制为22 。
 #3 = Class #22 // com/dskj/jvm/bytecode/MyTest1#22 = Utf8 com/dskj/jvm/bytecode/MyTest1从第27位开始的十六进制
十六进制字节码文件:
01 00 01 61 01 00 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65


推荐阅读