通过手工解析一个简单的 Java 程序所编译的 Class 文件,来了解,学习 Class 文件结构。
介绍编译环境和工具。
package com.hyl.learnerJVM.classtest;
/**
* class测试类
*
* @author hyl
* @version v1.0: ClassTest.java, v 0.1 2020/8/7 8:03 $
*/
public class ClassTest {
private long n;
public long inLong() {
return n + 1;
}
}
首先需要了解 class 文件的整体结构。
根据《Java 虚拟机规范》结构如下
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];
}
来自 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
接下来按照这个结构顺序解读。
u4:4 个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 CA FE BA BE
魔数(Magic Number),唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。(“cafe babe” 和 “Java” 也算是个彩蛋)
官方说明:
The magic item supplies the magic number identifying the class file format; it has the value 0xCAFEBABE.
u2:2 个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 00 00
次版本号,曾经在 Java 2 中出现过,以后,就再也没有被使用过了,基本上固定为零了。
u2:2 个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 00 34
主版本号,Java 版本从 45 开始,JDK 1.1 以后每个大版本加 1 ,这里 52(0x34)就是 JDK1.8 的意思。
官方说明:
The values of the minor_version and major_version items are the minor and major version numbers of this class file. Together, a major and a minor version number determine the version of the class file format. If a class file has major version number M and minor version number m, we denote the version of its class file format as M.m. Thus, class file format versions may be ordered lexicographically, for example, 1.5 < 2.0 < 2.1.
A Java Virtual Machine implementation can support a class file format of version v if and only if v lies in some contiguous range Mi.0 ≤ v ≤ Mj.m. The release level of the Java SE platform to which a Java Virtual Machine implementation conforms is responsible for determining the range.
Oracle’s Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1. support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive.*
u2:2 个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 00 16
常量池容量计数值,就是接下来有几个常量。这里是显示有 22(0x16)个,但是实际上只有 21 个,因为 第 0 个预留了,用于 “ 不引用任何一个常量池项目 ”,所以常量池的引用是从 1 开始。对于其他集合类型,一般都是从 0 下标开始的。
官方说明:
The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one. A constant_pool index is considered valid if it is greater than zero and less than constant_pool_count, with the exception for constants of type long and double noted in §4.4.5.
cp_info:常量表结构,长度是 constant_pool_count-1
。(从上文可知是 21 个)
结构如下:
cp_info {
u1 tag;
u1 info[];
}
tag 的类型如下:
类型 Constant Type | 标志 Value | 描述 |
---|---|---|
CONSTANT_Class |
7 | 类和接口的符号引用 |
CONSTANT_Fieldref |
9 | 字段的符号引用 |
CONSTANT_Methodref |
10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref |
11 | 接口中方法的符号引用 |
CONSTANT_String |
8 | 字符串类型字面量 |
CONSTANT_Integer |
3 | 整型字面量 |
CONSTANT_Float |
4 | 浮点型字面量 |
CONSTANT_Long |
5 | 长整型字面量 |
CONSTANT_Double |
6 | 双精度浮点型字面量 |
CONSTANT_NameAndType |
12 | 字段或方法的部分符号引用 |
CONSTANT_Utf8 |
1 | UTF-8 编码的字符串 |
CONSTANT_MethodHandle |
15 | 表示方法句柄 |
CONSTANT_MethodType |
16 | 表示方法类型 |
CONSTANT_InvokeDynamic |
18 | 表示一个动态方法调用 |
接下逐个常量做解读
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 0A 00 04 00 12
CONSTANT_Methodref
结构:
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
解读:
#1 = Methodref #4.#18
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 09
00000016 00 03 00 13
CONSTANT_Fieldref
结构:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
解读:
#2 = Fieldref #3.#19
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000016 07 00 14
CONSTANT_Class
结构:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
解读:
#3 = Class #20
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000016 07 00 15
CONSTANT_Class
结构:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
解读:
#4 = Class #21
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000016 01 00 01 6E
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#5 = Utf8 n
这里 utf-8 使用的是缩写编码。从
\u0001
到\u007f
之间的字符,使用一个字节(相当于1~127的ASCII码)。从\u0080
到\u07ff
之间采用两个字节。从\u0800
到\uffff
按照普通的 UTF-8 使用三个字节。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000016 01 00
00000032 01 4A
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#6 = Utf8 J
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000032 01 00 06 3C 69 6E 69 74 3E
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#7 = Utf8
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000032 01 00 03 28 29
00000048 56
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#8 = Utf8 ()V
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000048 01 00 04 43 6F 64 65
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#9 = Utf8 Code
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000048 01 00 0F 4C 69 6E 65 4E
00000064 75 6D 62 65 72 54 61 62 6C 65
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#10 = Utf8 LineNumberTable
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000064 01 00 12 4C 6F 63
00000080 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#11 = Utf8 LocalVariableTable
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000080 01
00000096 00 04 74 68 69 73
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#12 = Utf8 this
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000096 01 00 28 4C 63 6F 6D 2F 68 79
00000112 6C 2F 6C 65 61 72 6E 65 72 4A 56 4D 2F 63 6C 61
00000128 73 73 74 65 73 74 2F 43 6C 61 73 73 54 65 73 74
00000144 3B
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest;
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000144 01 00 06 69 6E 4C 6F 6E 67
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#14 = Utf8 inLong
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000144 01 00 03 28 29 4A
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#15 = Utf8 ()J
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000160 01 00 0A 53 6F 75 72 63 65 46 69 6C 65
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#16 = Utf8 SourceFile
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000160 01 00 0E
00000176 43 6C 61 73 73 54 65 73 74 2E 6A 61 76 61
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#17 = Utf8 ClassTest.java
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000176 0C 00
00000192 07 00 08
CONSTANT_NameAndType
结构:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
解读:
#18 = NameAndType #7:#8
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000192 0C 00 05 00 06
CONSTANT_NameAndType
结构:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
解读:
#19 = NameAndType #5:#6
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000192 01 00 26 63 6F 6D 2F 68
00000208 79 6C 2F 6C 65 61 72 6E 65 72 4A 56 4D 2F 63 6C
00000224 61 73 73 74 65 73 74 2F 43 6C 61 73 73 54 65 73
00000240 74
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#20 = Utf8 com/hyl/learnerJVM/classtest/ClassTest
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000240 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62
00000256 6A 65 63 74
CONSTANT_Utf8
结构:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
解读:
#21 = Utf8 java/lang/Object
u2:两个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 21
访问标志,识别一些类或接口的访问信息,包括,是类还是接口;是否是 public
;是否是 abstract
类型;是否声明 final
;等等。
访问标识如下:
Flag Name | Value | 含义 | Interpretation |
---|---|---|---|
ACC_PUBLIC |
0x0001 | 是否为public 类型 | Declared public ; may be accessed from outside its package. |
ACC_FINAL |
0x0010 | 是否被声明 final,只有类可设置 | Declared final ; no subclasses allowed. |
ACC_SUPER |
0x0020 | 是否允许使用invokespecial 字节码指令的新语义 | Treat superclass methods specially when invoked by the invokespecial instruction. |
ACC_INTERFACE |
0x0200 | 标识这是个接口 | Is an interface, not a class. |
ACC_ABSTRACT |
0x0400 | 是否为 abstract 类型 |
Declared abstract ; must not be instantiated. |
ACC_SYNTHETIC |
0x1000 | 标识这个类并非由用户代码产生的 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION |
0x2000 | 标识这是个注解 | Declared as an annotation type. |
ACC_ENUM |
0x4000 | 标识这是个枚举 | Declared as an enum type. |
原文 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1-200-E.1
解读:
access_flags 的值为:0x0001 | 0x0020 = 0x0021
flags: ACC_PUBLIC, ACC_SUPER
u2:两个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 03
类索引,确定这个类的全限名。指向一个 class 类型的常量。
官方说明:
The value of the this_class item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing the class or interface defined by this class file.
解读:
#3 = Class #20 // com/hyl/learnerJVM/classtest/ClassTest
u2:两个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 04
父类索引,确定这个父类的全限名。指向一个 class 类型常量。
官方说明:
For a class, the value of the super_class item either must be zero or must be a valid index into the constant_pool table. If the value of the super_class item is nonzero, the constant_pool entry at that index must be a CONSTANT_Class_info structure representing the direct superclass of the class defined by this class file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL flag set in the access_flags item of its ClassFile structure.
If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.
For an interface, the value of the super_class item must always be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure representing the class Object.
解读:
#4 = Class #21 // java/lang/Object
u2:两个字节
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 00
接口计数器,表示接口索引表的容量。
官方说明:
The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type.
解读:
这里接口数量为零,所以接下来的接口索引表不存在了。
u2:两个字节一组。
因为没有接口,所以这里在 class 文件中不存在。
接口索引表,包含实现的接口。
官方说明:
Each value in the interfaces array must be a valid index into the constant_pool table. The constant_pool entry at each value of interfaces[i], where 0 ≤ i < interfaces_count, must be a CONSTANT_Class_info structure representing an interface that is a direct superinterface of this class or interface type, in the left-to-right order given in the source for the type.
解读:
这里不存在。
u2:两个字节。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 01
字段表计数器,表示接下来有几个字段表。
官方说明:
The value of the fields_count item gives the number of field_info structures in the fields table. The field_info structures represent all fields, both class variables and instance variables, declared by this class or interface type.
解读:
接下来有一个字段表。
field_info: 字段表结构,长度是fields_count
,这里是一个。
结构如下:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags,字段修饰符,它与类的修饰符很像都是一个 u2 的数据类型。
字段访问标识含义:
Flag Name | Value | 含义 | Interpretation |
---|---|---|---|
ACC_PUBLIC |
0x0001 | 字段是否public |
Declared public ; may be accessed from outside its package. |
ACC_PRIVATE |
0x0002 | 字段是否private |
Declared private ; usable only within the defining class. |
ACC_PROTECTED |
0x0004 | 字段是否protected |
Declared protected ; may be accessed within subclasses. |
ACC_STATIC |
0x0008 | 字段是否static |
Declared static . |
ACC_FINAL |
0x0010 | 字段是否final |
Declared final ; never directly assigned to after object construction (JLS §17.5). |
ACC_VOLATILE |
0x0040 | 字段是否volatile |
Declared volatile ; cannot be cached. |
ACC_TRANSIENT |
0x0080 | 字段是否transient |
Declared transient ; not written or read by a persistent object manager. |
ACC_SYNTHETIC |
0x1000 | 字段是否由编译器自动产生 | Declared synthetic; not present in the source code. |
ACC_ENUM |
0x4000 | 字段是否enum |
Declared as an element of an enum . |
descriptor_index,描述符,描述数据类型、方法的参数列表(包括数量、类型以及顺序)和放回值。
描述符标识字符含义:
FieldType term | Type | 含义 | Interpretation |
---|---|---|---|
B |
byte |
基本类型byte |
signed byte |
C |
char |
基本类型char |
Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16 |
D |
double |
基本类型double |
double-precision floating-point value |
F |
float |
基本类型float |
single-precision floating-point value |
I |
int |
基本类型int |
integer |
J |
long |
基本类型long |
long integer |
L ClassName ; |
reference |
对象类型 | an instance of class ClassName |
S |
short |
基本类型short |
signed short |
Z |
boolean |
基本类型boolean |
true or false |
[ |
reference |
一个数组维度 | one array dimension |
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000256 00 02
00000272 00 05 00 06 00 00
字段 | 源码 | 说明 |
---|---|---|
access_flags | 00 02 | 字段是private |
name_index | 00 05 | #5 = Utf8 n 字段名为 n |
descriptor_index | 00 06 | #6 = Utf8 J 字段类型为 long |
attributes_count | 00 00 | 其他属性描述为空 |
attributes[attributes_count] | - | - |
解读:
private long n;
descriptor: J
flags: ACC_PRIVATE
u2:两个字节。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000272 00 02
方法表集合计数,记录接下里将会有几个方法表集合。
官方说明:
The value of the methods_count item gives the number of method_info structures in the methods table.
解读:
接下来会有两个方法表集合。
method_info:方法表结构,长度methods_count
,从上文可知,两个。
结构如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags,方法访问标志。
如下:
Flag Name | Value | 含义 | Interpretation |
---|---|---|---|
ACC_PUBLIC |
0x0001 | 方法是否为public |
Declared public ; may be accessed from outside its package. |
ACC_PRIVATE |
0x0002 | 方法是否为private |
Declared private ; accessible only within the defining class. |
ACC_PROTECTED |
0x0004 | 方法是否为protected |
Declared protected ; may be accessed within subclasses. |
ACC_STATIC |
0x0008 | 方法是否为static |
Declared static . |
ACC_FINAL |
0x0010 | 方法是否为final |
Declared final ; must not be overridden (§5.4.5). |
ACC_SYNCHRONIZED |
0x0020 | 方法是否为synchronized |
Declared synchronized ; invocation is wrapped by a monitor use. |
ACC_BRIDGE |
0x0040 | 方法是不是由编译器产生的桥接方法 | A bridge method, generated by the compiler. |
ACC_VARARGS |
0x0080 | 方法是否接受不定参数 | Declared with variable number of arguments. |
ACC_NATIVE |
0x0100 | 方法是否为native |
Declared native ; implemented in a language other than Java. |
ACC_ABSTRACT |
0x0400 | 方法是否为abstract |
Declared abstract ; no implementation is provided. |
ACC_STRICT |
0x0800 | 方法是否为strictfp |
Declared strictfp ; floating-point mode is FP-strict. |
ACC_SYNTHETIC |
0x1000 | 方法是由编译器自动产生 | Declared synthetic; not present in the source code. |
attributes,方法属性,以下是属性
Attribute | Location | class file |
---|---|---|
Code |
method_info |
45.3 |
Exceptions |
method_info |
45.3 |
RuntimeVisibleParameterAnnotations , RuntimeInvisibleParameterAnnotations |
method_info |
49.0 |
AnnotationDefault |
method_info |
49.0 |
MethodParameters |
method_info |
52.0 |
Synthetic |
ClassFile , field_info , method_info |
45.3 |
Deprecated |
ClassFile , field_info , method_info |
45.3 |
Signature |
ClassFile , field_info , method_info |
49.0 |
RuntimeVisibleAnnotations , RuntimeInvisibleAnnotations |
ClassFile , field_info , method_info |
49.0 |
完整属性列表:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000272 00 01 00 07 00 08 00 01
00000288 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7
00000304 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 01 00
00000320 00 00 09 00 0B 00 00 00 0C 00 01 00 00 00 05 00
00000336 0C 00 0D 00 00
字段对应:
access_flags | name_index | descriptor_index | attributes_count |
---|---|---|---|
00 01 | 00 07 | 00 08 | 00 01 |
ACC_PUBLIC |
#7 = Utf8 方法名 |
#8 = Utf8 ()V 返回类型viod |
有一个属性表集合 |
方法属性:
0x00 09
, #9 = Utf8 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];
}
对应解读:
类型 | 长度 | 源码 | 含义 |
---|---|---|---|
attribute_name_index |
u2 | 00 09 | #9 = Utf8 Code 表明是 Code 属性 |
attribute_length |
u4 | 00 00 00 2F | 0x2F ,47个长度 |
max_stack |
u2 | 00 01 | 操作数栈(Operand Stack)深度的最大值,1 |
max_locals |
u2 | 00 01 | 局部变量表所需的储存空间,1个变量槽(Slot) |
code_length |
u4 | 00 00 00 05 | 方法指令长度,5 个字节 |
code[5]
指令解读:
源码 | 字节码 | 含义 |
---|---|---|
2A | aload_0 | 将第一引用类型本地变量推送至栈顶 |
B7 00 01 | invokespecial #1 //Method java/lang/Object." |
调用超类构造方法,实例初始化方法,私有方法 |
B1 | return | 从当前方法返回 viod |
exception_table_length
,00 00,异常表为零。
exception_table[exception_table_length]
,就不存在啦。
attributes_count
,方法属性表集合数,00 02,两个,如下。
00 0A:
#10 = Utf8 LineNumberTable
,可知是LineNumberTable
属性。
LineNumberTable
属性,用于描述 Java 源码行号与字节码(字节码的偏移量)之间的对应关系。
结构如下:
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];
}
attribute_name_index | attribute_length | line_number_table_length |
---|---|---|
00 0A | 00 00 00 06 | 00 01 |
LineNumberTable |
6个字节长度 | 一个 |
line_number_table[1]
start_pc | start_pc |
---|---|
00 00 | 00 09 |
00 0B:
#11 = Utf8 LocalVariableTable
,可知是LocalVariableTable
属性。
LocalVariableTable
属性,用于描述栈帧中局部变量表的变量与 Java 源码中定义的变量之间的关系。
结构如下:
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];
}
attribute_name_index | attribute_length | local_variable_table_length |
---|---|---|
00 0B | 00 00 00 0C | 00 01 |
LocalVariableTable 属性 |
12个长度 | 1个值 |
local_variable_table[1]
start_pc | length | name_index | descriptor_index | index |
---|---|---|---|---|
00 00 | 00 05 | 00 0C | 00 0D | 00 00 |
0 | 5 | #12 = Utf8 this |
#13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest; |
0 |
start_pc
和length
属性分别代表了整个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖长度,结合起来就是局部变量在字节码之中的作用范围。
this
变量是系统默认添加进去的。
最终整个方法解读为:
public com.hyl.learnerJVM.classtest.ClassTest();
descriptor: ()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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/hyl/learnerJVM/classtest/ClassTest;
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000336 00 01 00 0E 00 0F 00 01 00 09 00
00000352 00 00 31 00 04 00 01 00 00 00 07 2A B4 00 02 0A
00000368 61 AD 00 00 00 02 00 0A 00 00 00 06 00 01 00 00
00000384 00 0E 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C
00000400 00 0D 00 00
字段对应:
access_flags | name_index | descriptor_index | attributes_count |
---|---|---|---|
00 01 | 00 0E | 00 0F | 00 01 |
ACC_PUBLIC |
#14 = Utf8 inLong 方法名 |
#15 = Utf8 ()J 返回类型 long |
有一个属性表集合 |
方法属性:
0x00 09
, #9 = Utf8 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];
}
对应解读:
类型 | 长度 | 源码 | 含义 |
---|---|---|---|
attribute_name_index |
u2 | 00 09 | #9 = Utf8 Code 表明是 Code 属性 |
attribute_length |
u4 | 00 00 00 31 | 0x31 ,49个长度 |
max_stack |
u2 | 00 04 | 操作数栈(Operand Stack)深度的最大值,4 |
max_locals |
u2 | 00 01 | 局部变量表所需的储存空间,1个变量槽(Slot) |
code_length |
u4 | 00 00 00 07 | 方法指令长度,7 个字节 |
code[7]
指令解读:
源码 | 字节码 | 含义 |
---|---|---|
2A | aload_0 | 将第一引用类型本地变量推送至栈顶 |
B4 00 02 | getfield #2 // Field n:J |
获得指定类的实例域,并将其值压入栈顶 |
0A | lconst_1 | 将 long 类型 1 推送至栈顶 |
61 | ladd | 将栈顶两个 long 类型值相加并将结果压入栈顶 |
AD | lreturn | 从当前方法返回 long |
exception_table_length
,00 00,异常表为零。
exception_table[exception_table_length]
,就不存在啦。
attributes_count
,方法属性表集合数,00 02,两个,如下。
00 0A:
#10 = Utf8 LineNumberTable
,可知是LineNumberTable
属性。
LineNumberTable
属性,用于描述 Java 源码行号与字节码(字节码的偏移量)之间的对应关系。
结构如下:
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];
}
attribute_name_index | attribute_length | line_number_table_length |
---|---|---|
00 0A | 00 00 00 06 | 00 01 |
LineNumberTable |
6个字节长度 | 一个 |
line_number_table[1]
start_pc | start_pc |
---|---|
00 00 | 00 0E |
00 0B:
#11 = Utf8 LocalVariableTable
,可知是LocalVariableTable
属性。
LocalVariableTable
属性,用于描述栈帧中局部变量表的变量与 Java 源码中定义的变量之间的关系。
结构如下:
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];
}
attribute_name_index | attribute_length | local_variable_table_length |
---|---|---|
00 0B | 00 00 00 0C | 00 01 |
LocalVariableTable 属性 |
12个长度 | 1个值 |
local_variable_table[1]
start_pc | length | name_index | descriptor_index | index |
---|---|---|---|---|
00 00 | 00 07 | 00 0C | 00 0D | 00 00 |
0 | 7 | #12 = Utf8 this |
#13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest; |
0 |
start_pc
和length
属性分别代表了整个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖长度,结合起来就是局部变量在字节码之中的作用范围。
this
变量是系统默认添加进去的。
最终整个方法解读为:
public long inLong();
descriptor: ()J
flags: ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field n:J
4: lconst_1
5: ladd
6: lreturn
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/hyl/learnerJVM/classtest/ClassTest;
u2:两个字节。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000400 00 01
class属性表集合的计数,这里有一个。
官方说明:
The value of the attributes_count item gives the number of attributes in the attributes table of this class.
解读:
接下里有一个文件的属性表集合
attribute_info:属性表结构,长度attributes_count
,从上文可知,一个。
结构如下:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
属性类型列表:
Attribute | Location | class file |
---|---|---|
SourceFile |
ClassFile |
45.3 |
InnerClasses |
ClassFile |
45.3 |
EnclosingMethod |
ClassFile |
49.0 |
SourceDebugExtension |
ClassFile |
49.0 |
BootstrapMethods |
ClassFile |
51.0 |
Synthetic |
ClassFile , field_info , method_info |
45.3 |
Deprecated |
ClassFile , field_info , method_info |
45.3 |
Signature |
ClassFile , field_info , method_info |
49.0 |
RuntimeVisibleAnnotations , RuntimeInvisibleAnnotations |
ClassFile , field_info , method_info |
49.0 |
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000400 00 10 00 00 00 02 00 11
结构 | 源码 | 解释 |
---|---|---|
attribute_name_index | 00 10 | 0x10,#16 = Utf8 SourceFile |
attribute_length | 00 00 00 02 | 两个字节 |
info[attribute_length] | 00 11 | 由下图结构可知,0x11,#17 = Utf8 ClassTest.java |
根据解读是SourceFile
,结构如下:
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
SourceFile
属性用于记录生成这个 Class 文件的源码文件名称。
解读:
SourceFile: "ClassTest.java"
指令
javap -verbose -p ClassTest.class
结果
Classfile /D:/git/mybase/projects/github/learner-JVM/target/classes/com/hyl/le arnerJVM/classtest/ClassTest.class
Last modified 2020-8-7; size 414 bytes
MD5 checksum cdd61b9867778e815b9d0b93b1412a9d
Compiled from "ClassTest.java"
public class com.hyl.learnerJVM.classtest.ClassTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."":()V
#2 = Fieldref #3.#19 // com/hyl/learnerJVM/classtest/Clas sTest.n:J
#3 = Class #20 // com/hyl/learnerJVM/classtest/Clas sTest
#4 = Class #21 // java/lang/Object
#5 = Utf8 n
#6 = Utf8 J
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest;
#14 = Utf8 inLong
#15 = Utf8 ()J
#16 = Utf8 SourceFile
#17 = Utf8 ClassTest.java
#18 = NameAndType #7:#8 // "":()V
#19 = NameAndType #5:#6 // n:J
#20 = Utf8 com/hyl/learnerJVM/classtest/ClassTest
#21 = Utf8 java/lang/Object
{
private long n;
descriptor: J
flags: ACC_PRIVATE
public com.hyl.learnerJVM.classtest.ClassTest();
descriptor: ()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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/hyl/learnerJVM/classtest/ClassTest;
public long inLong();
descriptor: ()J
flags: ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field n:J
4: lconst_1
5: ladd
6: lreturn
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/hyl/learnerJVM/classtest/ClassTest;
}
SourceFile: "ClassTest.java"