首先声明,本篇文章学习自深入理解JVM之Java字节码(.class)文件详解 - 简书 (jianshu.com)
以下仅为自我整理:
1.每个类或者接口都单独占据着一个class文件
2.class文件中的信息是一项一项排列的。
重点:IMPORTANT
class文件的结构
class文件可以被分为十个部分,有:
MagicNumber,Version,Constant_pool,Access_flag,This_class,Super_class,Interfaces,Fields,Methods 和Attributes
- magic
- 在class文件的开头的四个字节,此魔数是class文件的标志,是固定的值:0xCAFEBABE.它的作用主要就是来判断这个文件是不是class文件。
- minor_version & major_version
- 接着的四个字节是class文件的此版本号与主版本号。其他好像没有什么重要的。
- constant_pool
- very very important:常量池。其中存放了文字字符串,常量值,当前的类名,字段名,方法名,哥哥字段和方法的描述符,对当前类的字段和方法的引用信息,当前类中对其他类的引用信息等。class文件中的很多其他部分都是对常量池中的数据项的引用。字节码指令也存在对常量池的引用(作为一个操作数),常量池中的各个项也会相互引用。
- 常量池是一个勒的结构与索引,对对象的调用可以通过索引位置来代替。想要快速的获取某个变量常用的方法是通过索引变量,可以直观理解成“内存地址的虚拟”(这边好像不太懂)。
- 之所以称之为静态池:是因为这里维护者经过编译之后的相对固定的数据索引。
- constant_pool_count = 1,每个类只有一个常量池。常量池中的每个数据都是通过索引得到的,类似于数组,不过常量池的第一项索引为1而不是0.
- 每个数据叫做一个XXX_INFO项。每个info对应这一种tag,表示了info项的类型。(具体见上图)
- acesss flag
- 保存当前类的访问权限
- this_cases
- 保存当前类的全局限定名在常量池中的索引
- super_class
- 保存当前类的父类的全局限定名在常量池中的索引
- interfaces
- 保存当前类实现的接口列表。
- interfaces_count :当前类实现的接口数目
- interfaces[] :包含count个接口的全局限定名的索引的数组
- fields
- 保存当前类的成员列表(static修饰的成员)
- fields_count:类变量和实例变量的字段的数量总和
- fields[] :包含字段详细信息的列表
- methods
- 保存当前类的方法列表
- methods_count :该类活接口显示定义的方法的数量
- methods[] :包含方法信息的一个详细列表
- attributes
- 包含当前类的attributes列表
-
class文件的最后一部分是属性,它描述了该类或者接口所定义的一些属性信息。attributes_count指的是attributes列表中包含的attribute_info的数量。
属性可以出现在class文件的很多地方,而不只是出现在attributes列表里。如果是attributes表里的属性,那么它就是对整个class文件所对应的类或者接口的描述;如果出现在fileds的某一项里,那么它就是对该字段额外信息的描述;如果出现在methods的某一项里,那么它就是对该方法额外信息的描述。
(自己操作不了,就截一个图啦)
1.
2.
3. 常量池包含的常量的数目,常量池包含 1 * 16 + 2 - 1 = 17
4.
0a //tag 10表示这是一个CONSTANT_Methodref_info结构
00 04 //class_index 指向常量池中第4个常量所表示的类
00 0e //name_and_type_index 指向常量池中第14个常量所表示的方法
5. 09 00 03 00 0F
09 //tag
00 03 //指向常量池中第3个常量所表示的类
00 0f //指向常量池中第15个常量所表示的变量
6. 07 00 10
指向的是常量池中第16个常量所表示的class名称
7. 07 00 11
同上
8. 01 00 04 74 65 73 74
01 //tag 1表示这是一个CONSTANT_Utf8结构
00 04 //表示这个字符串的长度是4字节,也就是后面的四个字节74 65 73 74
74 65 73 74 //通过ASCII码表转换后,表示的是字符串“test”
完整的常量池
00 12 常量池的数目 18-1=17
0a 00 04 00 0e 方法:java.lang.Ojbect void ()
09 00 03 00 0f 方法 :Hello int test()
07 00 10 字符串:Hello
07 00 11 字符串:java.lang.Ojbect
01 00 04 74 65 73 74 字符串:test
01 00 01 49 字符串:I
01 00 06 3c 69 6e 69 74 3e 字符串:
01 00 03 28 29 56 字符串:()V
01 00 04 43 6f 64 65 字符串:Code
01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 字符串:LineNumberTable
01 00 03 28 29 49 字符串:()I
01 00 0a 53 6f 75 72 63 65 46 69 6c 65 字符串:SourceFile
01 00 0a 48 65 6c 6c 6f 2e 6a 61 76 61 字符串:Hello.java
0c 00 07 00 08 NameAndType: ()V
0c 00 05 00 06 NameAndType:test I
01 00 05 48 65 6c 6c 6f 字符串:Hello
01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 字符串: java/lang/Object
public class org.jvminternals.SimpleClass SourceFile: "SimpleClass.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#17 // java/lang/Object."":()V #2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #20 // "Hello" #4 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #23 // org/jvminternals/SimpleClass #6 = Class #24 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lorg/jvminternals/SimpleClass; #14 = Utf8 sayHello #15 = Utf8 SourceFile #16 = Utf8 SimpleClass.java #17 = NameAndType #7:#8 // " ":()V #18 = Class #25 // java/lang/System #19 = NameAndType #26:#27 // out:Ljava/io/PrintStream; #20 = Utf8 Hello #21 = Class #28 // java/io/PrintStream #22 = NameAndType #29:#30 // println:(Ljava/lang/String;)V #23 = Utf8 org/jvminternals/SimpleClass #24 = Utf8 java/lang/Object #25 = Utf8 java/lang/System #26 = Utf8 out #27 = Utf8 Ljava/io/PrintStream; #28 = Utf8 java/io/PrintStream #29 = Utf8 println #30 = Utf8 (Ljava/lang/String;)V { public org.jvminternals.SimpleClass(); Signature: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/jvminternals/SimpleClass; public void sayHello(); Signature: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String "Hello" 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 6: 0 line 7: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lorg/jvminternals/SimpleClass; }
9 00 21
表示的是这个变量的访问位置位
10. 00 03
这个表示的是this_class指向的是constant pool的索引值,此处为3表示的就是指向常量池的第三项,即“HELLO”.
11.00 04:super class
同上,表示的是第四个常量,指的是“java/lang/object”.
12. 00 00:interfaces
包含的是interfaces_count和interfaces[]两个字段,此处无接口故不存在相关的阿选项
13. 00 01 00 01 00 05 00 06 00 00:fields
00 01 fields count //表示成员变量的个数,此处为1个
00 02 00 05 00 06 00 00 //成员变量的结构
每个成员的变量对应一个field_info结构
field_info {
u2 access_flags; 0002 // ACC_PRIVATE
u2 name_index; 0005 // "test"
u2 descriptor_index; 0006 // "I"
u2 attributes_count; 0000 // "private int test"
attribute_info attributes[attributes_count]; // 没有附加属性
}
14。 00 02 00 01 00 07 00 08 00 01 00 09_______:methods
00 02:method_count(之所以会有两个方法,主要原因是JVM会自动挡阿生成一个
接着是两个method_info结构:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
前三个字段与field相同,分析得第一个方法是"public void
attribute_name_index为00 09,指向的是第九个常量,即code
00 01 ACC_PUBLIC
00 07
00 08 V()
code attribute的作用是保存该方法的结构所对应的字节码
attribute_length表示attribute所包含的字节数,这里为0000001d,即是39个字节,不包含attribute_name_index和attribute_length字段。
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];
}
max_stack表示这个方法运行的任何时刻所能达到的操作数栈的最大深度,这里是0001
max_locals表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量,这里是0001.
接下来的code_length表示该方法的所包含的字节码的字节数以及具体的指令码。
这里的字节码长度为00000005,即是后面的5个字节 2a b7 00 01 b1为对应的字节码指令的指令码。
15. attributes
最后剩下的内容是attributes,表示的是整个class文件的附加属性。结构类似attribute
SourceFile_attribute {
u2 attribute_name_index; // source
u4 attribute_length; // 2
u2 sourcefile_index; // hello。java
}
最后一个主要表明的是当前的class文件是由Hello.java文件编译而来的。
这一部分关于字节码的理解可以参考从字节码层面看“HelloWorld” - liuxiaopeng - 博客园 (cnblogs.com)



