class文件2-字节码阅读

本文在class文件1-文件格式的基础上,以阅读class文件的形式介绍常量池(Constant Pool) Method Attribute结构。

class文件的具体内容除了Constant_Pool Method Attribute,还有Interface Field,但是由于本文的示例代码没有接口和Field,故本文没有列出

Java文件源码,jdk版本是13.0.2,JVM是HotSpot,但是是通过IDEA进行build的

public class HelloWorld {
    public static void main(String[] args) {
        String hello = "Hello World";
        System.out.println(hello);
    }
}

对应的class文件如下:

cafe babe 0000 0039 0024 0a00 0200 0307
0004 0c00 0500 0601 0010 6a61 7661 2f6c
616e 672f 4f62 6a65 6374 0100 063c 696e
6974 3e01 0003 2829 5608 0008 0100 0b48
656c 6c6f 2057 6f72 6c64 0900 0a00 0b07
000c 0c00 0d00 0e01 0010 6a61 7661 2f6c
616e 672f 5379 7374 656d 0100 036f 7574
0100 154c 6a61 7661 2f69 6f2f 5072 696e
7453 7472 6561 6d3b 0a00 1000 1107 0012
0c00 1300 1401 0013 6a61 7661 2f69 6f2f
5072 696e 7453 7472 6561 6d01 0007 7072
696e 746c 6e01 0015 284c 6a61 7661 2f6c
616e 672f 5374 7269 6e67 3b29 5607 0016
0100 1b70 7269 2f6c 6a77 2f6a 6176 612f
6a76 6d2f 4865 6c6c 6f57 6f72 6c64 0100
0443 6f64 6501 000f 4c69 6e65 4e75 6d62
6572 5461 626c 6501 0012 4c6f 6361 6c56
6172 6961 626c 6554 6162 6c65 0100 0474
6869 7301 001d 4c70 7269 2f6c 6a77 2f6a
6176 612f 6a76 6d2f 4865 6c6c 6f57 6f72
6c64 3b01 0004 6d61 696e 0100 1628 5b4c
6a61 7661 2f6c 616e 672f 5374 7269 6e67
3b29 5601 0004 6172 6773 0100 135b 4c6a
6176 612f 6c61 6e67 2f53 7472 696e 673b
0100 0568 656c 6c6f 0100 124c 6a61 7661
2f6c 616e 672f 5374 7269 6e67 3b01 000a
536f 7572 6365 4669 6c65 0100 0f48 656c
6c6f 576f 726c 642e 6a61 7661 0021 0015
0002 0000 0000 0002 0001 0005 0006 0001
0017 0000 002f 0001 0001 0000 0005 2ab7
0001 b100 0000 0200 1800 0000 0600 0100
0000 0900 1900 0000 0c00 0100 0000 0500
1a00 1b00 0000 0900 1c00 1d00 0100 1700
0000 4700 0200 0200 0000 0b12 074c b200
092b b600 0fb1 0000 0002 0018 0000 000e
0003 0000 000c 0003 000d 000a 000e 0019
0000 0016 0002 0000 000b 001e 001f 0000
0003 0008 0020 0021 0001 0001 0022 0000
0002 0023 

此处是通过sublime打开class文件,为了展示美观自动添加了空格作为分隔符,实际上是不存在这些空格的,后文中也会重新按照对应的结构重新分割

反编译class文件展示如下

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package pri.ljw.java.jvm;

public class HelloWorld {
    public HelloWorld() {
    }

    public static void main(String[] args) {
        String hello = "Hello World";
        System.out.println(hello);
    }
}

Constant Pool

为了便于理解,首先通过IDEA的JClasslib概览一下Constant Pool内容
JClasslib_Constant_Pool

  • 常量池大小为35,对应的Constant_pool_count为36(0x0024)
字节码含义
0024常量池大小 Constant_pool_count

在《The Java® Virtual Machine Specification Java SE 13 Edition》文档的4.4节分12小节介绍了18种结构,本文只展示用到的6种结构,其余12种结合本文和官方文档查看也十分简单,此处不做赘述。

CONSTANT_Methodref_info

CONSTANT_Methodref_info,指向声明方法的类或接口的描述符
Constant_Methodref_info1. cp_info #2 <java/lang/Object> #2对应常量池的2号位(即 [02]CONSTANT_Class_info)
2. cp_info #3 <<init> : () V> #3对应常量池的3号位(即 [03]Constant_NameAndType_info)

// 总字节为5
CONSTANT_Methodref_info {
	u1 tag;	// 固定为10,即0x0a
	u2 class_index;
	u2 name_and_type_index;
}

[01]对应的字节码为:

0a 0002 0003
字节码含义
0atag -10,表示CONSTANT_Methodref_info
0002class_index - 2,对应常量池的2号位
0003name_and_type_index - 3,对应常量池的3号位

CONSTANT_Class_info

CONSTANT_Class_info,表示一个class或者interface
Constant_Class_info
#4对应到4号位([04]CONSTANT_Utf8_info),表示class名称信息

// 总字节为3
CONSTANT_Class_info {
	u1 tag;	// 固定为7,即0x07
	u2 name_index;
}

[02]对应的字节码为

07 0004
字节码含义
07tag - 7,表示CONSTANT_Class_info
0004name_index - 4,对应常量池的4号位

CONSTANT_NameAndType_info

CONSTANT_NameAndType_info,表示一个field或者method
Constant_NameAndType_info
同上,相关结构如下:

// 总字节为5
CONSTANT_NameAndType_info {
	u1 tag;	// 固定为12,即0x0c
	u2 name_index;
	u2 descriptor_index;
}

[03]对应的字节码为

0c 0005 0006
字节码含义
0ctag - 12,表示CONSTANT_NameAndType_info
0005name_index - 5,对应常量池的5号位
0006descriptor_index - 6,对应常量池的6号位

CONSTANT_Utf8_info

CONSTANT_Utf8_info,表示字符常量值

// 总字节为4
CONSTANT_Utf8_info {
	u1 tag;	// 固定为1,即0x01
	u2 length;
	u1 bytes[length];
}

下面分别对[04] [05] [06]三个常量位进行字节码翻译

  • [04]CONSTANT_Utf8_info对应的字节码
01 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374
字节码含义
01tag - 1,表示CONSTANT_Utf8_info
0010length - 16,当前表示className的长度(java/lang/Object)
6a…74bytes[length],对应具体的字符串java/lang/Object

java/lang/Object(UTF8编码)以16进制表示,即为

6a61 7661 2f6c 616e 672f 4f62 6a65 6374
  • [05]CONSTANT_Utf8_info对应的字节码
01 0006  3c69 6e69 743e 
字节码含义
01tag - 1,表示CONSTANT_Utf8_info
0006length - 6,当前表示方法名的长度
3c…3ebytes[length],对应具体的字符串<init>

<init>(UTF8编码)以16进制表示,即为

3c69 6e69 743e
  • [06]CONSTANT_Utf8_info字节码翻译如下
01 0003 2829 56
字节码含义
01tag - 1,表示CONSTANT_Utf8_info
0003length - 3,当前表示方法返回参数的长度
282956bytes[length],对应具体的字符串()V

()V(UTF8编码)以16进制表示,即为

2829 56

CONSTANT_String_info

CONSTANT_String_info,指向字符串对象

// 总字节为3
CONSTANT_String_info {
	u1 tag;	// 固定为8,即0x08
	u2 string_index;
}

[07]字节码为

08 0008 

[08]字节码为

0100 0b 4865 6c6c 6f20 576f 726c 64
字节码含义
08tag - 8,表示CONSTANT_String_info
0008string_index - 8,指向常量池8号位([08]CONSTANT_Utf8_info)
01tag - 1,表示CONSTANT_Utf8_info
000blength - 11,当前表示字符串长度(Hello World)
48…64bytes[length],对应具体的字符串Hello World

Hello World以16进制表示,即为

4865 6c6c 6f20 576f 726c 64

[07][08]对应的是Hello World字符串

CONSTANT_Fieldref_info

CONSTANT_Fieldref_info,指向声明Field的类或接口的描述符

// 总字节为5
CONSTANT_Fieldref_info {
	u1 tag;
	u2 class_index;
	u2 name_and_type_index;
}

[09]对应的字节码为

09 000a 000b
字节码含义
09tag - 9,表示CONSTANT_Fieldref_info
000aclass_index - 10,指向常量池10号位([10]CONSTANT_class_info)
000bname_and_type_index - 11,表示指向常量池10号位([10]CONSTANT_NameAndType_info)

至此,HelloWorld涉及到的常量池内容已翻译完毕,下文直接给出所有的常量位的字节码翻译

class文件的常量池字节码阅读

常量位字节码含义备注
010a 0002 0003Methodref,#2.# 3java/lang/Object.<init>:()V
0207 0004Class,#4java/lang/Object
030c 0005 0006NameAndType,#5:#6<init>:()V
0401 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374Utf8java/lang/Object
0501 0006 3c 696e 6974 3eUtf8<init>
0601 0003 2829 56Utf8()V
0708 0008StringHello World
0801 000b 48 656c 6c6f 2057 6f72 6c64Utf8Hello World
0909 000a 000bFieldref #10.#11java/lang/System.out:Ljava/io/PrintStream;
1007 000cClass #12java/lang/System
110c 000d 000eNameAndType #13:#14out:Ljava/io/PrintStream;
1201 0010 6a61 7661 2f6c 616e 672f 5379 7374 656dUtf8java/lang/System
1301 0003 6f 7574Utf8out
1401 0015 4c 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d3bUtf8Ljava/io/PrintStream;
150a 0010 0011Methodref,#16.#17java/io/PrintStream.println:(Ljava/lang/String;)V
1607 0012Class,#18java/io/PrintStream
170c 0013 0014NameAndType,#19:#20println:(Ljava/lang/String;)V
1801 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6dUtf8java/io/PrintStream
1901 0007 7072 696e 746c 6eUtf8println
2001 0015 284c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 56Utf8(Ljava/lang/String;)V
2107 0016Class,#22pri/ljw/java/jvm/HelloWorld
2201 001b 70 7269 2f6c 6a77 2f6a 6176 612f 6a76 6d2f 4865 6c6c 6f57 6f72 6c64Utf8pri/ljw/java/jvm/HelloWorld
2301 0004 43 6f64 65Utf8Code
2401 000f 4c69 6e65 4e75 6d62 6572 5461 626c 65Utf8LineNumberTable
2501 0012 4c6f 6361 6c56 6172 6961 626c 6554 6162 6c65Utf8LocalVariableTable
2601 0004 74 6869 73Utf8this
2701 001d 4c70 7269 2f6c 6a77 2f6a 6176 612f 6a76 6d2f 4865 6c6c 6f57 6f72 6c64 3bUtf8Lpri/ljw/java/jvm/HelloWorld;
2801 0004 6d61 696eUtf8main
2901 0016 28 5b4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 56Utf8([Ljava/lang/String;)V
3001 0004 6172 6773Utf8args
3101 0013 5b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673bUtf8[Ljava/lang/String;
3201 0005 68 656c 6c6fUtf8hello
3301 0012 4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3bUtf8Ljava/lang/String;
3401 000a 536f 7572 6365 4669 6c65Utf8SourceFile
3501 000f 48 656c 6c6f 576f 726c 642e 6a61 7661Utf8HelloWorld.java

以上就是常量池的字节码阅读,其中CodeLineNumberTableLocalVariableTable是JVM预定义好的属性字段

  • Code

    • Code属性是method_info结构的属性表中的可变长度属性
    • Code属性包含Java虚拟机指令和方法的辅助信息,包括实例初始化方法和类或接口初始化方法
    • 后文会在Method中看到源码阅读解析
    Code_attribute {
    	u2 attribute_name_index;
    	u4 attribute_length;
    	u2 max_stack;
    	u2 max_locals;
    	u4 code_length;
    	u1 code[code_length];
    	u2 exception_table_length;
    	{	u2 start_pc;
    		u2 end_pc;
    		u2 handler_pc;
    		u2 catch_type;
    	} exception_table[exception_table_length];
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }
    

    Code属性中的结构字段具体含义,可以参考《The Java® Virtual Machine Specification Java SE 13 Edition》文档的的4.7.3节。

  • LineNumberTable

    • LineNumberTable属性是Code属性的属性表中的一个可选的可变长度属性。 Debugger可以使用它来确定代码数组的哪一部分与原始源文件中的给定行号相对应。(可参考文档4.7.12)
    LineNumberTable_attribute {
    	u2 attribute_name_index;
    	u4 attribute_length;
    	u2 line_number_table_length;
    	{	u2 start_pc;
    		u2 line_number;
    	} line_number_table[line_number_table_length];
    }
    
  • LocalVariableTable

    • LocalVariableTable属性是Code属性的属性表中的一个可选的可变长度属性。 Debugger可以使用它来确定方法执行期间给定局部变量的值。(可参考文档4.7.13)
    LocalVariableTable_attribute {
    	u2 attribute_name_index;
    	u4 attribute_length;
    	u2 local_variable_table_length;
    	{	u2 start_pc;
    		u2 length;
    		u2 name_index;
    		u2 descriptor_index;
    		u2 index;
    	} local_variable_table[local_variable_table_length];
    }
    

Methods

Method表示当前类的方法信息,基本结构如下:

method_info {
	u2 access_flags;
	u2 name_index;
	u2 descriptor_index;
	u2 attributes_count;
	attribute_info attributes[attributes_count];
}

对应的字节码文件如下:

// 常量池内容
... ... ... 6c6f 576f 726c 642e 6a61 7661 
// access_flag this super interface field等内容
0021 0015 0002 0000 0000 
// method_count
0002
// 构造方法
0001 0005 0006 
// 构造方法的Code属性[基本信息 指令信息 LineNumberTable LocalVariableTable]
0001 0017 0000 002f 0001 0001 0000 0005 2a b7 0001 b1 0000 0002
0018 0000 0006 0001 0000 0009 
0019 0000 000c 0001 0000 0005 001a 001b 0000

按照构造方法和main方法分开进行字节码翻译

有关Java源文件行号,需要对应反编译后的class文件进行验证
JVM指令集相关内容,如指令对应的16进制及其含义,可参考JVM官方文档中的第6.4节
有关method的access flag信息可参考官方文档的Table 4.6 A
常量池${num}号位,简写为 #num,如:常量池24号位,简写为 #24
部分字段含义参考 class文件属性表解析

method_count 和 构造方法

序号字节码含义备注
10002method_count方法数量,2个,空构造方法和main方法
20001access_flag构造方法的访问修饰符,public
30005name_index#5,<init>,表示构造方法
40006descriptor_index#6,()V,构造方法的描述
50001attributes_count属性数量,1,Code
60017attribute_name_index#23,Code
70000 002fattribute_length属性值长度47
80001max_stack操作数栈的最大深度,此处为1
90001max_locals局部变量表所需要的存储空间,单位为slot
100000 0005code_length字节码指令长度,此处为5
112a指令0x2a,对应 aload_0,表示加载栈中的index为0的引用,指令长度累计为1
12b7指令0xb7,对应 invokespecial,指令长度累计为2
130001指令执行参数#1,对应空构造方法,指令长度累计为4
14b1指令0xb1,对应 return
150000exception_table_length异常表占用的字节数,此处为0
160002attributes_countCode属性的子属性个数,此处为2
170018attribute_name_index#24,LineNumberTable
180000 0006attribute_length属性值长度6
190001line_number_table_lengthline_number_table大小,此处为1,属性长度累计为2
200000start_pc对应Code属性的子属性数组中的下标,此处为0,属性长度累计为4
210009line_numberJava源文件行号,此处为9,属性长度累计为6
220019attribute_name_index#25,LocalVariableTable
230000 000cattribute_length属性值长度12
240001local_variable_table_lengthlocal_variable_table大小,此处为1
250000start_pc变量生命周期开始时的字节码偏移量,此处为0
260005length变量作用范围覆盖的字节数,此处为5
27001aname_index#26,this,表示变量名在常量池中的index
28001bdescriptor_index#27,Lpri/ljw/java/jvm/HelloWorld; ,描述该变量名含义的常量池index
290000index在当前栈桢中的位置

main方法

main方法的字节码内容紧接上文的构造方法,如下:

// main方法
0009 001c 001d 
// 构造方法的Code属性[基本信息 指令信息 LineNumberTable LocalVariableTable]
0001 0017 0000 0047 0002 0002 0000 000b 12 07 4c b2 0009 2b b6 000f b1 
0000 0002 0018 0000 000e 0003 0000 000c 0003 000d 000a 000e 
0019 0000 0016 0002 0000 000b 001e 001f 0000
0003 0008 0020 0021 0001
序号字节码含义备注
10009access_flag构造方法的访问修饰符,public static,ACC_PUBLIC(0x0001), ACC_STATIC(0x0008)
2001cname_index#28,main,表示main方法
3001ddescriptor_index#29,([Ljava/lang/String;)V,main方法的描述
40001attributes_count属性数量,1,Code
50017attribute_name_index#23,Code
60000 0047attribute_length属性值长度71
70002max_stack操作数栈的最大深度,此处为2
80002max_locals局部变量表所需要的存储空间,单位为slot
90000 000bcode_length字节码指令长度,此处为11
1012指令0x12,对应 ldc,压栈,此处表示将常量池中的Hello World压入栈
1107指令执行参数#7,String Hello World
124c指令0x4c,对应 astore_1,存储当前栈桢的index_1的引用
13b2指令0xb2,对应 getstatic,对应#9,
140009指令执行参数#9,Field java/lang/System.out:Ljava/io/PrintStream;
152b指令0x2b,对应 aload_1,加载index_1的引用
16b6指令0xb6,对应 invokevirtual,执行方法
17000f指令执行参数#15,Method java/io/PrintStream.println:(Ljava/lang/String;)V
18b1指令0xb1,对应 return
190000exception_table_length异常表占用的字节数,此处为0
200002attributes_countCode属性的子属性个数,此处为2
210018attribute_name_index#24,LineNumberTable
220000 000eattribute_length属性值长度14
230003line_number_table_lengthline_number_table大小,此处为3
240000start_pc字节码偏移量,此处为0
25000cline_numberJava源文件行号,此处为12
260003start_pc字节码偏移量,此处为3
27000dline_numberJava源文件行号,此处为13
28000astart_pc字节码偏移量,此处为10
29000eline_numberJava源文件行号,此处为14
300019attribute_name_index#25,LocalVariableTable
310000 0016attribute_length属性值长度22
320002local_variable_table_lengthlocal_variable_table大小,此处为2
330000start_pc变量生命周期开始时的字节码偏移量,此处为0
34000blength变量作用范围覆盖的字节数,此处为 11
35001ename_index#30,args,表示变量名在常量池中的index
36001fdescriptor_index#31,[Ljava/lang/String; ,描述该变量名含义的常量池index
370000index在当前栈桢中的位置
380003start_pc变量生命周期开始时的字节码偏移量,此处为3
390008length变量作用范围覆盖的字节数,此处为 8
400020name_index#32,hello,表示变量名在常量池中的index
410021descriptor_index#33,Ljava/lang/String; ,描述该变量名含义的常量池index
420001index在当前栈桢中的位置

Attributes

Attributes可以理解为一个结构单元,如ClassFile field_info method_info Code_attribute均会使用。
在上文中已经就method_info Code_attribute描述Attributes,本节就classFile进行阐述

字节码文件如下:

0001 0022 0000 0002 0023 

对应解释如下:

序号字节码含义备注
10001attribute_count属性个数,1
20022attribute_name_index#34,对应 SourceFile
30000 0002attribute_length属性长度,2
40023name_index#35,对应source file文件名,HelloWorld.java

总结

已经详细介绍了constant_pool method attribute相关class字节码阅读,补充一下其他未列出的字节码阅读,

// 常量池内容
6c6f 576f 726c 642e 6a61 7661 
// 常量池之后
0021 0015 0002 0000 0000 
// 方法和attribute
0002 0001 0005 0006 0001
字节码含义备注
0021access_flagspublic class, 隐形的含有acc_super
0015this_class常量池中的21号位,表示当前类
0002super_class常量池中的2号位,表示父类,对应的是Object类
0000interface_count实现的接口数,此处为0
0000fields_count字段数量

至此,以HelloWorld.java为例,对整个class文件的阅读进行详细描述,实际上整个阅读逻辑就是依据class文件结构,大家可以以此为主线,去翻译class字节码文件。

ClassFile { 
    u4 magic;  // 魔法数字,表明当前文件是.class文件,固定0xCAFEBABE
    u2 minor_version; // 为Class文件的副版本,现在基本固定为0x0000
    u2 major_version; // 为Class文件的主版本,对应jdk发行版本
    u2 constant_pool_count; // 常量池计数
    cp_info constant_pool[constant_pool_count-1];  // 常量池内容
    u2 access_flags; // 类访问标识
    u2 this_class; // 当前类
    u2 super_class; // 父类
    u2 interfaces_count; // 实现的接口数
    u2 interfaces[interfaces_count]; // 实现接口信息
    u2 fields_count; // 字段数量
    field_info fields[fields_count]; // 包含的字段信息 
    u2 methods_count; // 方法数量
    method_info methods[methods_count]; // 包含的方法信息
    u2 attributes_count;  // 属性数量
    attribute_info attributes[attributes_count]; // 各种属性
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值