JVM实战与原理---Class文件结构
JVM实战与原理
目录
Class文件结构
1. 数据结构
2. class文件结构详解
2.1 魔数与Class文件的版本
2.2 常量池
2.3 访问标志
2.4 类索引、父类索引与接口索引集合
2.5 字段表集合
2.6 方法表集合
2.7 属性表集合
Class文件结构
章节目的:介绍class文件的结构
引言:当使用javac命令对java文件进行编译后,便生成了Class文件,那么Class文件的结构是怎么样的呢?
下面是Class文件结构的介绍。
1. 数据结构
首先,我们需要知道Class文件的数据结构如下,其中u1、u2、u4分别代表1个字节、2个字节、4个字节,info结尾的代表一个复合结构的数据
ClassFile {u4 magic;u2 minor_version;u2 major_version;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];
}
2. class文件结构详解
接着,我们继续通过以下这段java代码来分析
class Person {private static String name = "wenxl";public static void main(String[] args) {System.out.println(name);}
}
对于已经编译生成了class文件,此时将class文件通过winHex打开,会有如下十六进制的字节码,例如首个CA就为一个字节,即为u1。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1500000000 CA FE BA BE 00 00 00 34 00 22 0A 00 07 00 13 09 漱壕 4 "
00000016 00 14 00 15 09 00 06 00 16 0A 00 17 00 18 08 00
00000032 19 07 00 1A 07 00 1B 01 00 04 6E 61 6D 65 01 00 name
00000048 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 Ljava/lang/Stri
00000064 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 ng; <init> (
00000080 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 )V Code Line
00000096 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 04 6D 61 NumberTable ma
00000112 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E in ([Ljava/lan
00000128 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 08 3C 63 g/String;)V <c
00000144 6C 69 6E 69 74 3E 01 00 0A 53 6F 75 72 63 65 46 linit> SourceF
00000160 69 6C 65 01 00 0B 50 65 72 73 6F 6E 2E 6A 61 76 ile Person.jav
00000176 61 0C 00 0A 00 0B 07 00 1C 0C 00 1D 00 1E 0C 00 a
00000192 08 00 09 07 00 1F 0C 00 20 00 21 01 00 05 77 65 ! we
00000208 6E 78 6C 01 00 06 50 65 72 73 6F 6E 01 00 10 6A nxl Person j
00000224 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 ava/lang/Object
00000240 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 java/lang/Syst
00000256 65 6D 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 em out Ljava
00000272 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B /io/PrintStream;
00000288 01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 java/io/Print
00000304 53 74 72 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E Stream println
00000320 01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 (Ljava/lang/S
00000336 74 72 69 6E 67 3B 29 56 00 20 00 06 00 07 00 00 tring;)V
00000352 00 01 00 0A 00 08 00 09 00 00 00 03 00 00 00 0A
00000368 00 0B 00 01 00 0C 00 00 00 1D 00 01 00 01 00 00
00000384 00 05 2A B7 00 01 B1 00 00 00 01 00 0D 00 00 00 *? ?
00000400 06 00 01 00 00 00 01 00 09 00 0E 00 0F 00 01 00
00000416 0C 00 00 00 26 00 02 00 01 00 00 00 0A B2 00 02 & ?
00000432 B2 00 03 B6 00 04 B1 00 00 00 01 00 0D 00 00 00 ? ? ?
00000448 0A 00 02 00 00 00 06 00 09 00 07 00 08 00 10 00
00000464 0B 00 01 00 0C 00 00 00 1E 00 01 00 00 00 00 00
00000480 06 12 05 B3 00 03 B1 00 00 00 01 00 0D 00 00 00 ? ?
00000496 06 00 01 00 00 00 03 00 01 00 11 00 00 00 02 00
00000512 12
2.1 魔数与Class文件的版本
ClassFile {u4 magic;u2 minor_version;u2 major_version;.......
}
对应的字节码为:CA FE BA BE 00 00 00 34
作用:每个Class文件的头4个字节称为魔数magic,作用是确定这个文件是否为JVM能接受的Clcass文件,固定为0xCAFEBABE。
后4字节说明Class文件的版本
字节码详解:00 00 00 34 说明该文件版本为JDK1.8。
2.2 常量池
ClassFile {.......u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1];.......
}
对应的字节码为:00 22
作用:前两个字节说明常量池的数量,后面则是常量池内容。常量池可以理解是Class文件的资源仓库。
字节码详解:其中前两个字节0x0022转换为十进制为34,说明有33个常量
cp_info常量类型的数据结构为
cp_info {u1 tag; u1 info[];
}
其中tag的值代表当前常量属于那种常量类型
Tag Value |
Constant Type |
DESC | DATA STRUCTURE |
1 | CONSTANT_Utf8 | utf-8编码的字符串 |
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; } |
3 | CONSTANT_Integer | 整形 |
CONSTANT_Integer_info { u1 tag; u4 bytes; } |
4 | CONSTANT_Float | 浮点型 |
CONSTANT_Float_info { u1 tag; u4 bytes; } |
5 | CONSTANT_Long | 长整形 |
CONSTANT_Long_info { u1 tag; u4 high_bytes; u4 low_bytes; } |
6 | CONSTANT_Double | 双精度浮点型 |
CONSTANT_Double_info { u1 tag; u4 high_bytes; u4 low_bytes; } |
7 | CONSTANT_Class | 类或接口的符号引用 |
CONSTANT_Class_info { u1 tag; u2 name_index; } |
8 | CONSTANT_String | 字符串类型 |
CONSTANT_String_info { u1 tag; u2 string_index; } |
9 | CONSTANT_Fieldref | 字段的符号引用 |
CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; } |
10 | CONSTANT_Methodref | 类中方法的符号引用 |
CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; } |
11 | CONSTANT_InterfaceMethodref | 接口中方法的符号引用 |
CONSTANT_InterfaceMethodref_info { u1 tag; u2 class_index; u2 name_and_type_index; } |
12 | CONSTANT_NameAndType | 字段或方法的部分符号引用 |
CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; } |
15 | CONSTANT_MethodHandle | 表示方法句柄 |
CONSTANT_MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index; } |
16 | CONSTANT_MethodType | 标识方法类型 |
CONSTANT_MethodType_info { u1 tag; u2 descriptor_index; } |
18 | CONSTANT_InvokeDynamic | 表示一个动态方法调用点 |
CONSTANT_InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; } |
我们按字节顺序开始分析,第一个tag值为0x0A,对应常量类型为CONSTANT_Methodref,其结构为
CONSTANT_Methodref_info {u1 tag;u2 class_index;u2 name_and_type_index;
}
对应的字节码为
0A00 0700 13
0007为class_index指向一个CONSTANT_Class_info
0013为name_and_type_index指向一个CONSTANT_NameAndType
那指向的这两个又是什么呢?我们往下继续分析
第二个tag值为0x09,对应常量类型为CONSTANT_Fieldref,其结构为
CONSTANT_Fieldref_info {u1 tag;u2 class_index;u2 name_and_type_index;
}
对应的字节码为
0900 1400 15
0014为class_index指向一个CONSTANT_Class_info
0015为name_and_type_index指向一个CONSTANT_NameAndType
我们按这样的逻辑将常量池字 节码解析为
Index | Tag | Info | Tag Value |
1 | 0A | 0007 0013 | CONSTANT_Methodref |
2 | 09 | 0014 0015 | CONSTANT_Fieldref |
3 | 09 | 0006 0016 | CONSTANT_Fieldref |
4 | 0A | 0017 0018 | CONSTANT_Methodref |
5 | 08 | 0019 | CONSTANT_String |
6 | 07 | 001A | CONSTANT_Class |
7 | 07 | 001B | CONSTANT_Class |
8 | 01 | 0004 6E6D 6561 | CONSTANT_Utf8 |
9 | 01 | 0012 4C.. ..3B | CONSTANT_Utf8 |
10 | 01 | 0006 3C.. ..3E | CONSTANT_Utf8 |
11 | 01 | 0003 2829 56 | CONSTANT_Utf8 |
12 | 01 | 0004 436F 6465 | CONSTANT_Utf8 |
13 | 01 | 000F 4C.. ..65 | CONSTANT_Utf8 |
14 | 01 | 0004 6D61 696E | CONSTANT_Utf8 |
15 | 01 | 0016 28.. ..56 | CONSTANT_Utf8 |
16 | 01 | 0008 3C.. ..3E | CONSTANT_Utf8 |
17 | 01 | 000A 53.. ..65 | CONSTANT_Utf8 |
18 | 01 | 000B 50.. ..61 | CONSTANT_Utf8 |
19 | 0C | 000A 000B | CONSTANT_NameAndType |
20 | 07 | 001C | CONSTANT_Class |
21 | 0C | 001D 001E | CONSTANT_NameAndType |
22 | 0C | 0008 0009 | CONSTANT_NameAndType |
23 | 07 | 001F | CONSTANT_Class |
24 | 0C | 0020 0021 | CONSTANT_NameAndType |
25 | 01 | 0005 77.. ..6C | CONSTANT_Utf8 |
26 | 01 | 0006 50.. ..6E | CONSTANT_Utf8 |
27 | 01 | 0010 6A.. ..74 | CONSTANT_Utf8 |
28 | 01 | 0010 6A.. ..6D | CONSTANT_Utf8 |
29 | 01 | 0003 6F75 74 | CONSTANT_Utf8 |
30 | 01 | 0015 4C.. ..3B | CONSTANT_Utf8 |
31 | 01 | 0013 6A.. ..6D | CONSTANT_Utf8 |
32 | 01 | 0007 70.. ..6E | CONSTANT_Utf8 |
33 | 01 | 0015 28.. ..56 | CONSTANT_Utf8 |
这时我们再回到第一个CONSTANT_Methodref,该常量用来描述的方法,其中
0007为class_index指向一个CONSTANT_Class_info,说明是哪个类的方法
0013为name_and_type_index指向一个CONSTANT_NameAndType,说明的是这个方法的方法名与方法的入参及返回值类型
0007即index为7的常量,我们可以看到是
7 | 07 | 001B | CONSTANT_Class |
CONSTANT_Class的结构为
CONSTANT_Class_info {u1 tag;u2 name_index;
}
同理,index为001B的即第27个常量是
27 | 01 | 0010 6A.. ..74 | CONSTANT_Utf8 |
CONSTANT_Utf8的结构为
CONSTANT_Utf8_info {u1 tag;u2 length;u1 bytes[length];
}
length为0010,即后面跟着长度为16的字符串,6A.. ..74转换为对应的字符串为java/lang/Object,说明调用的是java/lang/Object的方法
同样,0013指向第19个常量,是个CONSTANT_NameAndType_info
19 | 0C | 000A 000B | CONSTANT_NameAndType |
结构如下
CONSTANT_NameAndType_info {u1 tag;u2 name_index;u2 descriptor_index;
}
000A指向第10个常量,转换为对应的字符串为<init>,说明方法名为<init>
10 | 01 | 0006 3C.. ..3E | CONSTANT_Utf8 |
000B指向第11个常量,转换为对应的字符串为()V,说明入参为空,返回值类型是void。
我们可以用javap -verbose Person的命令反编译,就可以得到javap工具帮我们解析的字节码描述,我们发现其中Constant pool部分与我们分析出的结果是一致的。
Classfile /D:/用户目录/我的文档/Code/JVMSearch/Person.classLast modified 2020-12-19; size 513 bytesMD5 checksum dea507ac0ecc8c9063c778c6f748a2bbCompiled from "Person.java"
class Personminor version: 0major version: 52flags: ACC_SUPER
Constant pool:#1 = Methodref #7.#19 // java/lang/Object."<init>":()V#2 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream;#3 = Fieldref #6.#22 // Person.name:Ljava/lang/String;#4 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = String #25 // wenxl#6 = Class #26 // Person#7 = Class #27 // java/lang/Object#8 = Utf8 name#9 = Utf8 Ljava/lang/String;#10 = Utf8 <init>#11 = Utf8 ()V#12 = Utf8 Code#13 = Utf8 LineNumberTable#14 = Utf8 main#15 = Utf8 ([Ljava/lang/String;)V#16 = Utf8 <clinit>#17 = Utf8 SourceFile#18 = Utf8 Person.java#19 = NameAndType #10:#11 // "<init>":()V#20 = Class #28 // java/lang/System#21 = NameAndType #29:#30 // out:Ljava/io/PrintStream;#22 = NameAndType #8:#9 // name:Ljava/lang/String;#23 = Class #31 // java/io/PrintStream#24 = NameAndType #32:#33 // println:(Ljava/lang/String;)V#25 = Utf8 wenxl#26 = Utf8 Person#27 = Utf8 java/lang/Object#28 = Utf8 java/lang/System#29 = Utf8 out#30 = Utf8 Ljava/io/PrintStream;#31 = Utf8 java/io/PrintStream#32 = Utf8 println#33 = Utf8 (Ljava/lang/String;)V
{Person();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: getstatic #3 // Field name:Ljava/lang/String;6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: returnLineNumberTable:line 6: 0line 7: 9static {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=0, args_size=00: ldc #5 // String wenxl2: putstatic #3 // Field name:Ljava/lang/String;5: returnLineNumberTable:line 3: 0
}
SourceFile: "Person.java"
2.3 访问标志
ClassFile {.......u2 access_flags;.......
}
对应的字节码为:00 20
作用:用于识别类或者接口层次的访问信息
字节码详解:每个位所代表的意义如下表,因为该类是JDK1.2之后的编译器进行编译,且其他标识均为0,故为00 20。大家可以试试在类被声明为public,abstract,final的情况下,字节码是如何展示的。
Flag Name | Value | Interpretation |
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_FINAL | 0x0010 | 是否被声明为final,只有类可设置 |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令的新语意,JDK1.0.2该指令语意发生改变,故JDK1.0.2后该标志都为真 |
ACC_INTERFACE | 0x0200 | 标识这是一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型 |
ACC_SYNTHETIC | 0x1000 | 标识这个类并非由用户代码产生 |
ACC_ANNOTATION | 0x2000 | 标识这是一个注解 |
ACC_ENUM | 0x4000 | 标识这是一个枚举 |
2.4 类索引、父类索引与接口索引集合
ClassFile {.......u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];.......
}
对应的字节码为:00 06 00 07 00 00
作用:这三项数据确定了这个类的继承关系,类索引(this_class)确定类的全限定名,父类索引(super_class)确定类的父类的全限定名,接口索引集合(interfaces)描述类实现了哪些接口
字节码详解:00 06指向常量池第6个常量,类型为CONSTACT_Class_info,值为Person。
00 07同样类型为CONSTACT_Class_info,值为java/lang/Object。
00 00说明该类没实现任何接口。好奇的同学可以试试实现了接口后,这里的字节码是如何展示的。
2.5 字段表集合
ClassFile {.......u2 fields_count;field_info fields[fields_count];.......
}
对应的字节码为:00 01 00 0A 00 08 00 09 00 00
作用:描述接口或者类中的声明的变量。
字节码详解:
其中前两个字节0x0001转换为十进制为1,说明有1个字段。
field_info常量类型的数据结构为
field_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count];
}
access_flag为字段访问标志,含义表如下。字节码为00 0A,说明ACC_PRIVATE和ACC_STATIC为真,即该字段为private和static
Flag Name | Value | Interpretation |
ACC_PUBLIC | 0x0001 | 字段是否public |
ACC_PRIVATE | 0x0002 | 字段是否private |
ACC_PROTECTED | 0x0004 | 字段是否protected |
ACC_STATIC | 0x0008 | 字段是否static |
ACC_FINAL | 0x0010 | 字段是否final |
ACC_VOLATILE | 0x0040 | 字段是否volatile |
ACC_TRANSIENT | 0x0080 | 字段是否transient |
ACC_SYNTHETIC | 0x1000 | 字段是否由编译器自动产生的 |
ACC_ENUM | 0x4000 | 字段是否enum |
name_index代表字段的简单名称,descriptor_index代表字段和方法的描述符。
字节码为00 08 00 09,指向常量池第8和第9个常量,类型为CONSTACT_Utf8_info,值分别为name和Ljava/lang/String;。
说明该字段名为name,描述符为java/lang/String。
attribute_info则是该字段的属性表,字节码为00 00, 说明没有属性。
2.6 方法表集合
ClassFile {.......u2 methods_count;method_info methods[methods_count];.......
}
对应的字节码为:00 03 00 00 00 0A 00 0B 00 01
作用:描述接口或者类中的方法。
字节码详解:
其中前两个字节0x0003转换为十进制为3,说明有3个方法。
method_info常量类型的数据结构为
method_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count];
}
access_flag为字段访问标志,含义表如下。字节码为00 00,说明标志均为假
Flag Name | Value | Interpretation |
ACC_PUBLIC | 0x0001 | 方法是否public |
ACC_PRIVATE | 0x0002 | 方法是否private |
ACC_PROTECTED | 0x0004 | 方法是否protected |
ACC_STATIC | 0x0008 | 方法是否static |
ACC_FINAL | 0x0010 | 方法是否final |
ACC_SYNCHRONIZED | 0x0020 | 方法是否synchronized |
ACC_BRIDGE | 0x0040 | 方法是否是由编译器产生的桥接方法 |
ACC_VARARGS | 0x0080 | 方法是否接受不定参数 |
ACC_NATIVE | 0x0100 | 方法是否native |
ACC_ABSTRACT | 0x0400 | 方法是否abstract |
ACC_STRICT | 0x0800 | 方法是否strictfp |
ACC_SYNTHETIC | 0x1000 | 方法是否是由编译器自动产生的 |
字节码为00 0A 00 0B,指向常量池第10和第11个常量,类型为CONSTACT_Utf8_info,值分别为<init>和()V。name_index代表方法名,descriptor_index代表方法的描述符。
说明该方法名为<init>,描述符为()V。
attribute_info则是该字段的属性表,那么属性表是什么样的结构呢。
2.7 属性表集合
ClassFile {u2 attributes_count;attribute_info attributes[attributes_count];.......
}
对应的字节码为:00 01 00 0C 00 00 00 1D
作用:Class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息
字节码详解:
其中前两个字节0x0001转换为十进制为1,说明有1个属性。
attribute_info常量类型的数据结构为
attribute_info {u2 attribute_name_index;u4 attribute_length;u1 info[attribute_length];
}
attribute_name_index为属性名。字节码为00 0C,指向常量池第12个常量,类型为CONSTACT_Utf8_info,值为Code,说明这是一个Code属性。
00 00 00 1D说明后面属性内容的长度,1D为十进制的29,说明后面29个字节的内容为属性内容。那么这个Code属性的属性作用和结构内容是怎么样的呢?
Code属性作用:方法中的代码经过编译器变为的字节码指令,存储在Code属性内。
Code属性表结构:
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];
}
字节码详解:00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0D
这里从max_stack开始,max_stack描述了操作数栈深度的最大值,这里是00 01说明最大值为1
max_locals描述了局部变量表所需的存储空间,单位是Slot,对于double和long这两种64位的需要两个Slot存放,其余的byte、char、float、int、short、boolean和returnAddress等长度不超过32位的则用1个Slot存放。
Slot可以被重用,当代码执行超过局部变量的作用域,该变量占用的Slot可以被其他Slot占用,所以max_locals不等于方法用到了多少个局部变量,而是编译器根据变量的作用域计算出max_locals的大小。
code_length和code存储Java源程序编译后生成的字节码指令,code_length为00 05,说明后面有5个字节为字节码指令,为2A B7 00 01 B1,根据指令表,2A对应指令为aload_0,含义是将第0个Slot中为reference类型的本地变量推送到操作数栈顶;B7对应指令为invokespecial,作用是以栈顶的reference类型的数据所指向的对象作为方法接收者,调用对象的实例构造方法,后跟两个字节是他的参数,说明调用哪一个方法;00 01是参数,指向常量池第1个常量,类型为CONSTACT_Methodref_info,值为java/lang/Object."<init>":()V;B1对应指令为return,含义是返回此方法,且返回值为void,执行该条指令后,方法结束。
exception_table用于实现Java一场及finally处理机制,00 00说明定义了0个异常。
最后的是attribute_info,说明Code属性的属性,00 01说明有1个属性,结构是一样的。
字节码为00 0D,指向常量池第13个常量,类型为CONSTACT_Utf8_info,值为LineNumberTable,说明这是一个LineNumberTable属性。
LineNumberTable属性作用:描述Java源码行号与字节码行号之间的对应关系
LineNumberTable属性表结构:
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];
}
字节码详解:00 00 00 06 00 01 00 00 00 01
00 00 00 06说明后面6个字节为属性描述。
line_number_table_length说明line_number_table长度,00 01说明后续有1个line_number_table_length。
line_number_table包含两个数据项,其中start_pc是字节码行号,line_number是Java源码行号。
00 00 00 01说明行号数据是0:1。
还有很多属性,如下。
每个属性都有不同的结构及作用,读者可以自行修改源码,从而发现不同的属性。
属性名称 | 使用位置 | 含义 |
Code | 方法表 | Java代码编译成的字节码指令 |
ConstantValue | 字段表 | final关键字定义的常量值 |
Deprecated | 类、方法表、字段表 | 被声明为deprecated的方法和字段 |
Exceptions | 方法表 | 方法抛出的异常 |
EnclosingMethod | 类文件 | 仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法 |
InnerClasses | 类文件 | 内部类列表 |
LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
LocalVariableTable | Code属性 | 方法的局部变量描述 |
StackMapTable | Code属性 | JDK1.6新增属性,供新的类型检查验证器检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配 |
Signature | 类、方法表、字段表 | 用于支持泛型情况下的方法签名 |
SourceFile | 类文件 | 记录源文件名称 |
SourceDebugExtension | 类文件 | 存储额外的调试信息 |
Synthetic | 类、方法表、字段表 | 标识方法或字段为编译器自动生成的 |
LocalVariableTypeTable | 类 | 使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 |
RuntimeVisibleAnnotations | 类、方法表、字段表 | 为动态注解提供支持,指明哪些注解是运行时可见的 |
RuntimeInvisibleAnnotations | 类、方法表、字段表 | 指明哪些注解是运行时不可见的 |
RuntimeVisibleParameterAnnotations | 方法表 | 与RuntimeVisibleAnnotations类似,作用对象为方法参数 |
RuntimeInvisibleParameterAnnotations | 方法表 | 与RuntimeInvisibleAnnotations类似,作用对象为方法参数 |
AnootationDefault | 方法表 | 记录注解类元素的默认值 |
BootstrapMethods | 类文件 | 保存invokedynamic指令引用的引导方法限定符 |
后面的字节码也无需在分析下去了,后面还有两个方法,一个属性,按照这个结构继续解析下去,就可以发现,跟javap反编译出来的描述是一致的。
Classfile /D:/用户目录/我的文档/Code/JVMSearch/Person.classLast modified 2020-12-19; size 513 bytesMD5 checksum dea507ac0ecc8c9063c778c6f748a2bbCompiled from "Person.java"
class Personminor version: 0major version: 52flags: ACC_SUPER
Constant pool:#1 = Methodref #7.#19 // java/lang/Object."<init>":()V#2 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream;#3 = Fieldref #6.#22 // Person.name:Ljava/lang/String;#4 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = String #25 // wenxl#6 = Class #26 // Person#7 = Class #27 // java/lang/Object#8 = Utf8 name#9 = Utf8 Ljava/lang/String;#10 = Utf8 <init>#11 = Utf8 ()V#12 = Utf8 Code#13 = Utf8 LineNumberTable#14 = Utf8 main#15 = Utf8 ([Ljava/lang/String;)V#16 = Utf8 <clinit>#17 = Utf8 SourceFile#18 = Utf8 Person.java#19 = NameAndType #10:#11 // "<init>":()V#20 = Class #28 // java/lang/System#21 = NameAndType #29:#30 // out:Ljava/io/PrintStream;#22 = NameAndType #8:#9 // name:Ljava/lang/String;#23 = Class #31 // java/io/PrintStream#24 = NameAndType #32:#33 // println:(Ljava/lang/String;)V#25 = Utf8 wenxl#26 = Utf8 Person#27 = Utf8 java/lang/Object#28 = Utf8 java/lang/System#29 = Utf8 out#30 = Utf8 Ljava/io/PrintStream;#31 = Utf8 java/io/PrintStream#32 = Utf8 println#33 = Utf8 (Ljava/lang/String;)V
{Person();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: getstatic #3 // Field name:Ljava/lang/String;6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: returnLineNumberTable:line 6: 0line 7: 9static {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=0, args_size=00: ldc #5 // String wenxl2: putstatic #3 // Field name:Ljava/lang/String;5: returnLineNumberTable:line 3: 0
}
SourceFile: "Person.java"
JVM实战与原理---Class文件结构相关推荐
- JVM实战与原理--前言及索引
JVM实战与原理 大纲 在Java入门课中,我们编写HelloWorld.java文件,用命令行运行javac后生成HelloWorld.class,之后用java命令运行HelloWorld.cla ...
- JVM实战与原理---类加载机制
JVM实战与原理 目录 类加载机制 1. 类加载生命周期 1.1 加载 1.2 验证 1.3 准备 1.4 解析 1.5 初始化 2. 类加载器 类加载机制 章节目的:了解虚拟机如何加载Class文件 ...
- JVM实战与原理---内存回收策略
JVM实战与原理 目录 内存回收策略 1. 堆的划分 2. 判断对象是否存活的算法介绍 2.1 引用计数算法 2.2 可达性分析算法 3. 可回收的内存区域清理的算法介绍 3.1 标记-清除算法 3. ...
- JVM实战与原理---字节码执行引擎
JVM实战与原理 目录 字节码执行引擎 1. 方法区 2. 栈帧 2.1 局部变量表 2.2 操作数栈 2.3 动态连接 2.4 方法返回地址 字节码执行引擎 章节目的:虚拟机是如何找到正确的方法,如 ...
- JVM实战与原理---内存区域分配
JVM实战与原理 目录 内存区域分配 1. 程序计数器 2. Java虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区 6. 运行时常量池 内存区域分配 章节目的:明白虚拟机中的内存是如何划分?每块 ...
- 029、JVM实战总结:大厂面试题:最新的G1垃圾回收器的工作原理,你能聊聊吗
1.ParNew + CMS的组合让我们有哪些痛点? 痛点:STW,且停顿时间不可控 G1垃圾回收器比~更好的垃圾回收性能 2.G1垃圾回收器 G1 同时回收新生代和老年代的对象,把java堆拆分为多 ...
- 030、JVM实战总结:G1分代回收原理深度图解:为什么回收性能比传统GC更好?
本文是<从 0 开始带你成为JVM实战高手>内容总结,版权问题,特此声明 1.前文回顾 很多Region,新老各自对应一些Region,会在指定的系统停顿时间内,回收价值最大的Region ...
- JVM内存Dump原理与在线分析实战 | 得物技术
1.前言 当前我们微服务容器化部署JVM 实例很多,常常需要进行JVM heap dump analysis,为了提升JVM 问题排查效率,得物技术保障团队研究了JVM内存Dump 原理与设计开发了J ...
- 开放下载!阿里云开发者学堂配套教材《JVM实战》
简介:Java虚拟机不仅是一种跨平台的软件,而且是一种新的网络计算平台.该平台包括许多相关的技术,如符合开放接口标准的各种API.优化技术等. 在学习Java 虚拟机的时候你可能有以下疑问: Java ...
最新文章
- 年度书单盘点 | “裁员潮”持续蔓延?职场没有铁饭碗,只有硬技能
- 利用mem数组完成MM32 的 MicroPython中UART1的(REPL)的交互
- on条件与where条件的区别
- Spring MVC 4.1 支持jsonp
- mqtt客户端_初次接触MQTT
- SAP Spartacus 服务器端渲染编程规范
- mysql数据库子查询的使用_MySQL数据库使用子查询方式更新数据优化及思考
- 0x08算法设计与分析复习(二):算法设计策略-回溯法2
- SAP Business One助力洛德集团实现巨大商业价值
- 刷新table数据_关于数据透视表的刷新功能最值得了解的几个操作
- C++走向远洋——43(人数不定的工资类,动态分配内存与释放)
- html js实现分页代码,js分页代码示例
- 20145322何志威《网络对抗》逆向及Bof基础
- WinSCP显示隐藏文件
- activity劫持学习与复现
- Python开发【十一章】:RabbitMQ队列
- “AI+机器人”持续为多领域增“智”添“质”,开启效益增长飞轮
- Tanzu系列:第8部分 - 创建tkg集群
- 汇编指令学习(AND,OR,XOR,NOT)
- 【图像去噪】基于matlab小波变换(硬阙值+软阙值)图像去噪【含Matlab源码 391期】