目录
  1. 1. ClassFile结构
    1. 1.1. ClassFile结构体
      1. 1.1.1. magic
      2. 1.1.2. minor_version与major_version
      3. 1.1.3. constant_pool_count
      4. 1.1.4. constant_pool[constant_pool_count-1]
      5. 1.1.5. access_flags
        1. 1.1.5.1. 接口通过设置ACC_INTERFACE标志来被区分
        2. 1.1.5.2. ACC_SUPER
        3. 1.1.5.3. ACC_SYNTHETIC
        4. 1.1.5.4. ACC_ENUM
      6. 1.1.6. this_class
      7. 1.1.7. super_class
      8. 1.1.8. interfaces_count
      9. 1.1.9. interfaces[interfaces_count]
      10. 1.1.10. fields_count
      11. 1.1.11. fields[fields_count]
      12. 1.1.12. methods_count
      13. 1.1.13. methods[methods_count]
      14. 1.1.14. attributes_count
      15. 1.1.15. attributes[attributes_count]
        1. 1.1.15.1. 被定义的属性
        2. 1.1.15.2. 属性结构定义
    2. 1.2. 文献引用
jvm内存模型

ClassFile结构

ClassFile结构体

一个class文件由一个ClassFile结构组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ClassFile {
u4 magic; // class文件的魔数,用来验证class文件的格式是否正确,它的值为0xCAFEBABE
u2 minor_version; // class文件的小版本号
u2 major_version; // class文件的大版本号
u2 constant_pool_count; // 常量池索引数
cp_info constant_pool[constant_pool_count-1]; // 常量池数组,存储所有常量
u2 access_flags; // 访问修饰符标志(public,final,super,interface,abstract, synthetic,annotation,enum)
u2 this_class; // 当前类,这个值必须是在constant_pool表中一个有效索引,这个索引下的值必须是一个CONSTANT_Class_info结构,它表示class文件中类或接口被定义
u2 super_class; // 父类,这个值必须为0或者必须是在constant_pool表中一个有效索引,当为0时,class表示为一个没有父类的类对象;是常量池索引时,表示class文件定义的类的直接父类
u2 interfaces_count; // 接口数,表示这个类或接口直接父接口数量
u2 interfaces[interfaces_count]; // 接口数组,表示这个类所有的直接父接口
u2 fields_count; // 字段数(字段包括类变量,实例变量,申明类或接口的类型)
field_info fields[fields_count]; // class中所有的字段
u2 methods_count; // 方法数
method_info methods[methods_count]; // class中所有的方法
u2 attributes_count; // 属性数
attribute_info attributes[attributes_count]; // class中所以有的属性
}

magic

魔数,是class文件的第一个字段,主要用来识别class文件是否能被jvm虚拟机识别。
魔数值为:0xCAFEBABE
使用魔数而不是扩展名进行识别主要是基于安全考虑,因为扩展名容易被改动。

minor_version与major_version

minor_version与major_version一起组成class文件的版本号,这个版本号能可以让我们知道class文件是哪个版本的jvm编译的。

  1. jdk与major对应版本
1
2
3
4
5
6
7
8
J2SE 8      = 52
J2SE 7 = 51
J2SE 6.0 = 50
J2SE 5.0 = 49
JDK 1.4 = 48
JDK 1.3 = 47
JDK 1.2 = 46
JDK 1.1 = 45
  1. 使用javap获取class文件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
E:\code\test\target\classes\com\example\test\test>javap -v TestApplication.class
Classfile /E:/code/test/target/classes/com/example/test/test/TestApplication.class
Last modified 2020-1-2; size 743 bytes
MD5 checksum dc0a6ae2f5edfed8ea91866691b95593
Compiled from "TestApplication.java"
public class com.example.test.test.TestApplication
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // com/example/test/test/TestApplication
#3 = Methodref #23.#24 // org/springframework/boot/SpringApplication.run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;
#4 = Class #25 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 Lcom/example/test/test/TestApplication;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 MethodParameters
#17 = Utf8 SourceFile
#18 = Utf8 TestApplication.java
#19 = Utf8 RuntimeVisibleAnnotations
#20 = Utf8 Lorg/springframework/boot/autoconfigure/SpringBootApplication;
#21 = NameAndType #5:#6 // "<init>":()V
#22 = Utf8 com/example/test/test/TestApplication
#23 = Class #26 // org/springframework/boot/SpringApplication
#24 = NameAndType #27:#28 // run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;
#25 = Utf8 java/lang/Object
#26 = Utf8 org/springframework/boot/SpringApplication
#27 = Utf8 run
#28 = Utf8 (Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;
{
public com.example.test.test.TestApplication();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/test/test/TestApplication;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #2 // class com/example/test/test/TestApplication
2: aload_0
3: invokestatic #3 // Method org/springframework/boot/SpringApplication.run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;
6: pop
7: return
LineNumberTable:
line 10: 0
line 11: 7
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 args [Ljava/lang/String;
MethodParameters:
Name Flags
args
}
SourceFile: "TestApplication.java"
RuntimeVisibleAnnotations:
0: #20()

constant_pool_count

常量池元素数等于常量池表最大索引值加1。如果一个常量池索引值大于0,小于constant_pool_count,那它是有效的索引。

constant_pool[constant_pool_count-1]

常量池数组,这是一个包含多种结构的表,它表示在ClassFile结构和它的子结构中的多种string常量,class和interface名,字段名和其他的常量。
每个常量表项通过第一个tag字节表示一个格式。
这个常量池表的最大索引值为constant_pool_count-1

access_flags

访问标志,用来确定类或者接口的访问权限和属性。
access_flag

接口通过设置ACC_INTERFACE标志来被区分
  1. 如果ACC_INTERFACE标志被设置,则ACC_ABTRACT标志必须也被设置,这个class将被定义为一个接口,并且ACC_FINAL,ACC_SUPER,ACC_ENUM将不会被设置(因为final修饰不能被继承或实现,super表示有父类,接口没有父类,enum是一个枚举类型,与类和接口不一样)。
  2. 如果ACC_INTERFACE标志没有被设置,这个class将被定义为一个类而不是接口,除了ACC_ANNOTATION外,其他的标志都可能会被设置(因为只有接口才能成为注解);如果这个类是一个实体类,ACC_ABTRACT将不会被设置;如果设置ACC_FINAL,表示这个类不能被继承。
ACC_SUPER
  1. 如果ACC_SUPER出现在类或者接口中,invokespecial指令被调用,ACC_SUPER有两种语义被表示。
  2. 在早期编译器中,ACC_SUPER会被忽略掉,但是在java 8 及以上版本的编译器则会在每个类中设置ACC_SUPER标志。
  3. ACC_SUPER用来表示如何调用父类,早期版本编译器使用invokenonvirtual指令(invokespecial前身,未设置ACC_SUPER)来调用父类方法,这个指令是编译时绑定,当编译后父类被更改,但不重新编译,这时依然会运行之前绑定的方法;但是使用invokespecial指令(设置了ACC_SUPER)则会搜索类层次,找到最近一个父类进行方法调用,得到正确结果。
ACC_SYNTHETIC
  1. 这个标志表示这个类或者接口通过编译器被生成,并且不会出现在源代码中。
ACC_ENUM
  1. 这个标志表示这个类或者它的子类被定义为一个枚举类型。

this_class

  1. this_class元素的值必须是在常量池表中的一个有效索引,这个索引对应的常量池项必须是一个CONSTANT_Class_info结构(类结构),表示在class文件中一个类或者接口被定义。

super_class

对于一个类

  1. super_class元素的值被设置为0或者其值是在常量池中的一个有效索引。
  2. 如果super_class的值不为0,那么这个值在常量池中对应的索引对应的常量池项是一个CONSTANT_Class_info结构,它被表示class文件定义了这个类的直接父类。
  3. 如果这个类没有一个或者多个直接父类,那么这个类可能被设置ACC_FINAL标志。
  4. 如果super_class元素值为0,这个class文件必须表示为这个类对象本身,并且这个类或者接口没有一个直接父类。
  5. 对于一个接口,super_class的值必须总是在常量池中的一个有效值。这个索引对应的常量池项必须是一个CONSTANT_Class_info结构,它表示这个类对象本身。

interfaces_count

  1. 接口数,表示这个类或者接口的直接父接口数量。

interfaces[interfaces_count]

  1. 在interfaces数组中的值都是在常量池中的一个有效索引。
  2. 每个有效索引对应的常量池项必须是一个CONSTANT_Class_info结构,它表示为这个类或接口的一个直接父接口。interfaces中索引从小到大表示一个类的接口从左到右。

fields_count

  1. fields_count的值表示在字段表中的field_info结构数量。
  2. field_info结构表示所有的字段,包括类变量,实例变量,申明类或接口的类型。

fields[fields_count]

  1. 在字段表中的每个值必须是一个field_info结构,它是在一个类或者接口中一个字段的完整描述。
  2. 这个字段表包括了在这个类或者接口中被申明的所有字段。
  3. 这个字段表不包括从父类或父接口继承的字段。

methods_count

  1. methods_count元素的值表示在methods表中的method_info结构数量。

methods[methods_count]

  1. 在methods表中的每个值是一个method_info结构,它是在这个类或接口中的一个方法的完整描述。
  2. 如果在一个method_info结构中的access_flags中ACC_NATIVE和ACC_ABSTRACT标志都没被设置(表示这个方法不是本地方法,也不是抽象方法),java虚拟机提供了实现该方法的指令。
  3. method_info结构表示在类或者接口中被申明的所有方法,包括实例方法,类方法,实例初始化方法和一些类或接口的初始化方法。
  4. 这个方法表不包括从父类或者父接口继承的方法。

attributes_count

  1. attributes_count元素的值表示在attributes表中的属性数

attributes[attributes_count]

  1. 在属性表中的每个值都是一个attribute_info结构。
被定义的属性

attributes

属性结构定义
1
2
3
4
5
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}

文献引用

ACC_SUPER和早期的invokespecial

文章作者: rack-leen
文章链接: http://yoursite.com/2019/12/31/Java/jvm/class%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/ClassFile%E7%BB%93%E6%9E%84/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 rack-leen's blog
打赏
  • 微信
  • 支付宝

评论