十六进制编辑器(手机16进制编辑器)
00
// java/io/PrintStream.println:(Ljava/lang/String;)V
0B
1FA ~ 1FB:0x0001,属性个数为 1;
0x0004
aload_0
<init>
?表 2.2.2-6 CONSTANT_Utf8_info 型常量的结构?
03
3 = String
1
含义
?表 2.2.2-1 常量池?
bytes
2 个字节能够表示的最大值为 65535,占用字节数最大不能超过 65535,即要小于 64 KB,否则无法通过编译。
length
11: iload_1 // 将第 2 个变量槽的值复制到操作数栈顶 {2} {this, 2, e}
13
名称
attribute_length
标志值
0A
?表 2.2.6-8 异常表结构?
07
u2
00
u1 类型
类型
ACC_ABSTRACT
}
1A
类或接口的符号引用
ACC_TRANSIENT
表 2.2-1 class 文件结构?
Deprecated
final
I = 4;
line_number_table
表示一个动态计算常量
24 = Utf8
00 ~ 03:0xCAFEBABE,这 4 个字节称为魔数(magic),用来确定此文件是否为一个能被虚拟机接受的 class 文件。[1]
0x0001:引导方法数量为 1。
class 文件是一组以 8 个字节为基础单位的二进制流[1],只有两种数据类型:无符号数、表。
1
attribute_length
18
ACC_BRIDGE
local_variable_info
int i1 = 3;
...
Address
// {操作数栈最大深度 1} {局部变量表同时生存的局部变量所占最大的槽数 5} {参数个数 1}
含义
private
B1
0F
0A
5: iconst_1
00
00
0E
1
名称
将 int 类型的 1 推送至栈顶
u2
名称
0A
13
30
08 ~ 09:常量的数量(constant_pool_count),0x0027,十进制为 39,代表常量池中有 38 项常量,索引值为 1 ~ 38。[1]
Code:
00000000:
30 = NameAndType
08
0F
01
1
// java/lang/System.out:Ljava/io/PrintStream;
fields
标志位 7
15: return
Z
字节码
00
attributes
?表 2.2.7-8 bootstrap_method_table 结构?
8.25
} finally {
1
u2
ACC_PUBLIC
u2
74
在 Java 代码层面上的方法特征签名只包括方法名称和参数的个数、类型、顺序;而在字节码中的特征签名还包括方法返回值及受检异常表,方法的描述符包括参数列表和返回值。
158 ~ 16C:0x284C6A6176612F6C616E672F537472696E673B2956,经 UTF-8 解码,得 (Ljava/lang/String;)V。
local_variable_table_length:局部变量个数。
line_number_table_length
1
1
0x0080
数量
07
exception_table_length
04
29:0x07,十进制为 7,第 8 项常量的标志位,属于 CONSTANT_Class_info 类型。
12
0x0000000C:转十进制为 12,属性剩余长度为 12 个字节。
字节码
u2
4: istore_1
8: return
u2
0x000B,指向第 11 项常量,第 11 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 I,所以此字段类型为 int。
07
Address
69
00
u2
start_pc
0x03
int i2 = i1;
0B
1D5 ~ 1D6:0x0002,操作数栈的深度的最大值为 2。
([Ljava/lang/String;)V
183 ~ 184:0x000D,此字段的描述符索引,指向第 13 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Ljava/lang/String;”,此字段的类型为 java.lang.String。
final static String STR = new String(&34;Hello&34;);也不会有 ConstantValue 属性,在 <clinit> 方法中赋值。
0C
// --- catch ---
getstatic 指令的参数,代表一个符号引用,指向第 5 项 CONSTANT_Fieldref_info 类型的常量:java/lang/System.out:Ljava/io/PrintStream;,说明获取的是 System 类型的 out 变量
00
0C
0xB5
0x0020 的二进制为 0000 0000 0010 0000,若类使用 invokespecial 指令的新语义,则第 6 位应为 1,否则为 0。
0x0100
5: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, 1}
1
01
flags:
01
00
4
4 = Fieldref
x = 1;
接着是下一个方法表;
u4
描述
1F0 ~ 1F1:0x0002,行号表长度为 2。
03
ACC_PRIVATE
00
8
10 ~ 11:0x0008,指向第 8 项常量。
00
01
end_pc
6C
名称
08
引导方法的数量
含义
000001F0:
0x0020
attribute_length
index:局部变量在栈帧的局部变量表中变量槽的位置。当变量数据类型大于 32 位时,它占用的变量槽为 index、index + 1 这两个。
00000160:
UTF-8 编码的字符串
JDK5 引入泛型后,新增属性 LocalVariableTypeTable,仅将 descriptor_index 替换了字段的特征签名(Signature)[1],signature_index 是一个索引,指向 CONSTANT_Utf8_info 型常量。
// 如果 [0, 4) 出现 Exception 异常,跳转至 8:
descriptor_index
LineNumberTable:
B2
10
名称
1
4: iconst_3 // 将 int 类型的 3 复制到操作数栈顶 {3} {this, 1, 1}
2F
12
?表 2.2.6-4 属性结构?
04
1
1
06
?表 2.2.5-4 描述符字符含义?
Address
08
CONSTANT_Double_info
00
CONSTANT_NameAndType_info
0x003D:转十进制为 61...
00
static
0x0010
1
1
double、long 这两种 64 位的数据类型,使用两个变量槽存放。[1]
C
01
u2
attributes
u2
17D ~ 17E:属性数量(attributes_count),0x0000,这里为 0,自然就没有属性表了。
1
1
21: aload
34:35
u2
// x = 3;
0x04
36 = Utf8
09
0x0080
13 = Utf8
ACC_INIERFACE
这是 putfield 指令的参数,代表一个符号引用,指向第 2 项 CONSTANT_Fieldref_info 型常量:Happy.i:I,应为 Happy 类的 int 类型的变量名为 i 的变量赋值
将局部变量表中的第 1 个变量槽中的引用类型的本地变量推送至栈顶[4]
minor version: 0
类型
fields_count
0x0400
09
09
?表 2.2.7-3 Exceptions 属性结构?
native
1
1: getfield
16D ~ 16E:0x0030,访问标志(access_flags),表示类型的修饰符。
00000180:
00
name_index:局部变量名索引
1
0x0036:转十进制为 54,指向第 54 项 CONSTANT_MethodHandle_info 型常量,代表一个方法句柄,REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(...)... 代表调用某静态方法。
模块
53
u4
6E
0x0001
1
17: astore
18F ~ 190:0x0001,方法的属性数量为 1。
1
attribute_length
0x0010
double
attribute_length
interfaces_count
1
07
CONSTANT_Package_info
0C
2
class_index
u2
如果存在异常表,则结构如下:
CONSTANT_Module_info
0x0002
01
name_index
1BD ~ 1BE:0x0001,源码行号 1。
1CD ~ 1CE:0x0001,方法的属性数量为 1。
04
00
6C
56
17 any若 try 中没有抛出异常,返回 1;若 try 中抛出的异常可被 catch 捕获,跳转至 8:,返回 2;若抛出的异常没有被捕获,跳转至 17,将异常上抛给调用者;无论是否抛出异常,finally 都会执行。
ldc
1
Ljava/io/PrintStream;
03
0x0004
?码 2.2.5-1 class 反编译后的字段表内容?
方法体里面的代码经过 javac 编译后,最终变为字节码指令存储在 Code 属性内,结构如下:
public static void main(java.lang.String[]);
从当前方法返回 void
1
01
11
00
1D7 ~ 1D8:0x0001,局部变量表的存储空间为 1。
// --- try ---
1
1CB ~ 1CC:0x0013,此方法的描述符索引,指向第 19 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ([Ljava/lang/String;)V,一个返回值类型为 void,形参为 String[] 类型的方法。
com/cqh/arr3/FieldResolution has been compiled by a more recent version of the Java Runtime (class file version 60.0),
09
0x2A
1
第 2 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 i,int 类型。
constantvalue_index
00
line 2: 4
I
CONSTANT_Float_info
00
descriptor_index
?码 2.2.6-2 方法表集合?
20
// --- finally ---
12
10 = Utf8
1
CONSTANT_Fieldref_info
max_stack
0x0001 的二进制为 0000 0000 0000 0001,若类是 public 类型,则第 1 位应为 1,否则为 0。
指向 CONSTANT_Utf8_info 型常量,值为 “BootstrapMethods”
0x0010
这段字节码代表的含义:调用 Object 的无参构造方法,为 this 指向的对象的 int 类型的实例变量 i 赋值 1,为 this 指向的对象的 String 类型的实例变量 name 赋值 “java”,然后方法结束,返回值为 void。
字段的符号引用
method_info
为栈顶数据指向的对象的实例变量赋值(显式初始化)
61
0xB2
Code
attribute_length
name
例:
?表 2.2.7-5 local_variable_table 表结构?
final static int I;
public
from
attribute_name_index
18
标志值
float
00
0D
SourceFile
000001F0:
B7
不是运行必需的属性,可以编译时使用参数 -g:none 或 -g:vars 取消此项信息。
UTF-8 编码的字符串占用字节数
ACC_PROTECTED
public
1E8 ~ 1E9:0x0001,属性数量为 1。
03
?码 2.2.7-3 非字面量形式赋值:调用方法?
综合,此字段为 String name;。
0: getstatic
0D
CONSTANT_Methodref_info
descriptor: ([Ljava/lang/String;)V
000001B0:
local_variable_table_length
00000010:
Exception in thread &34;main&34; java.lang.UnsupportedClassVersionError:
attribute_name_index
29 = Class
this_class
1B3 ~ 1B4:属性名称的索引(attribute_name_index),0x0011,指向第 17 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “LineNumberTable”,此属性描述 Java 源码行号与字节码行号(字节码的偏移量)之间的对应关系。
类文件
u2
// 将 1 重新读到操作数栈顶,准备返回
x = 2;
// x = 1;对应两个指令
S
00000180:
// x = 2;
flags: ACC_PUBLIC
使用 invokespecial 字节码指令的新语义,JDK 1.0.2 后都为真
08
21 = Utf8
4: aload_0
00
code_length
6: iload_2 // 将第 3 个变量槽的值复制到操作数栈顶 {1} {this, 3, 1}
20 = Utf8
?表 2.2.5-3 字段访问标志?
7
invokevirtual
B5
6 = String
00000190:
04
000001A0:
00
[1] 使用魔数作为文件格式的标识,因为扩展名可以随意改动,不安全。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过而且不会引起混淆。
attribute_count
ACC_TRANSIENT
// java/lang/System
public void some() {
16 = Utf8
0F:第 2 项常量的标志位 0x09,十进制为 9,查表可知这个常量属于 CONSTANT_Fieldref_info 类型,代表字段的符号引用。
接受可变长参数
000001E0:
Hello World
short
23: athrow // 抛出异常
含义
ACC_PUBLIC
0F
00
2A
?码 2.2.6-1 异常处理源码及反编译后的指令?
0B
u2
1
00
数量
[2] 虽然它是一个 u4 类型的长度值,理论上最大值可以达到 232,但是《Java虚拟机规范》中明确限制了一个方法不允许超过 65535 条字节码指令,即它实际只使用了 u2 的长度,如果超过这个限制,javac 编译器就会拒绝编译。
u2
0x0400
1B5 ~ 1B8:0x0000000E,属性值长度为 14;从 1B9 ~ 1C6 都是此属性的内容。这个属性表的长度为 14 + 6 = 20 个字节,从 1B3 ~ 1C6。
0: iconst_1 // 将 int 类型的常量 1 加载到操作数栈顶 {1} {this}
00
[3] 受篇幅原因,其余类型常量的结构,可在 《Java 虚拟机规范》的 4.4 节查看。
类型
0x0000 的每位都是 0,此字段没有修饰符。
attributes_count
76
// String Hello World
8.23
out
04
// --- throw 异常 ---
2: return?表 2.2.7-6 ConstantValue 属性结构?
2F
Ljava/lang/String;
从当前方法返回 void
20
// Method java/io/PrintStream.println:(Ljava/lang/String;)V
1
对象类型,如 Ljava/lang/Object;
}此字段表不会有 ConstantValue 属性,在 <clinit> 方法中赋值。
01
max_locals
1 = Methodref
00
描述
[2] class 文件里,所有的 “.” 都被斜杆 “/” 代替,例 “java.lang.Object” 变为 “java/lang/Object”。
putfield
0D
06
ldc
0E
00
1EC ~ 1EF:0x0000000A,属性值长度为 10:1EF ~ 1F8。
stack=2, locals=1, args_size=1
00
super_class
第 4 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 name,java.lang.String 类型。
constantvalue_index:此索引指向常量池中一个字面量常量,根据字段类型不同,可以是 CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、 CONSTANT_Integer_info和CONSTANT_String_info 常量中的一种。
调用栈顶数据所指向的对象的实例初始化方法、父类方法、私有方法,后跟一个 u2 类型的参数说明调用哪个方法
u2
00
影响:所有参数名称都会丢失,IDE 会使用 arg0、arg1 之类的占位符代替原有参数名,在调试期间无法根据参数名称从上下文中获取参数值。
00
1BF ~ 1C0:0x0004,字节码行号 4。
invokespecial
u2
class_index
在 javac 编译时,把对 this 关键字的访问转变为对一个普通方法参数的访问,在 JVM 调用实例方法时自动传入此参数。
1
6A
使用 javap 反编译工具,加 -verbose 参数输出 class 文件的部分内容:
9 = Class
u2
0xB1
// 将 1 重新读到操作数栈顶,准备返回
// println:(Ljava/lang/String;)V
0x0040
u1
1FC ~ 1FD:0x0014,属性名称的索引(attribte_name_index),指向第 20 项 CONSTANT_Utf8_info 类型的常量:“SourceFile”,此属性用于记录生成 class 文件的源码文件名称。
00
u1
0A
ACC_SUPER
attribute_name_index
1
[1] 空出的一项为 0,将索引值设为 0,代表不引用任何一个常量池项目。
类型
15: iload_3 // 将第 4 个变量槽的值复制到操作数栈顶 {2} {this, 3, e, 2}
00
此索引指向 CONSTANT_Utf8_info 类型的常量,表示字符串字面量
flags: ACC_FINAL, ACC_SUPER
将 int、float 或 String 类型的常量值从常量池中推送至栈顶
0x0001
0x0040
接口中的方法的符号引用
数量
u1
名称
00
00
2A ~ 2B:0x001F,十进制为 31,指向第 31 项常量。
171 ~ 172:0x0009 这两个字节称为父类索引(super_class),用于确定父类的全限定名,除了 java.lang.Object 外,所有类都有父类,索引都不为 0。[1]
descriptor: I
05
1FE ~ 1E1:0x00000002,属性值长度(attribute_length)为 2。
00
u1
表示一个模块
?表 2.2.6-5 不同属性的含义?
fields_count
transient
CONSTANT_String_info
字段或方法的部分符号引用
Java虚拟机的做法是将局部变量表中的变量槽进行重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占的变量槽可以被其他局部变量所复用,javac 编译器会根据变量的作用域来分配变量槽给各个变量使用,根据同时生存的最大局部变量数量和类型计算出 max_locals 的大小。
接下来是第 2 个字段;
7 = Methodref
27 = NameAndType
1BB ~ 1BC:0x0000,字节码行号 0。
0x0800
9: iconst_2 // 将常量 2 复制到操作数栈顶 {2} {this, ?, e}
05
access_flags
u2
00
LocalVariableTable 属性
02
名称
09
类型
00
line 6: 8
01
0x2A
0x0007
1
双精度浮点型字面量
u2
获取指定类的类变量,并将其值压入操作数栈顶
0x06
1C
标识字符
u2
println
09
0C
35 = Utf8
0x0020
line_number_info
0D
000001C0:
6
197 ~ 198:0x0002,操作数栈(Operand Stack)深度的最大值(max_stack)。JVM 运行的时候需要根据这个值来分配栈帧(Stack Frame)中的操作栈深度。
8 = Class
Address
0xB5
0x2000
10
1F4 ~ 1F5:0x0005,源码行号 5。
u2
此索引指向 CONSTANT_NameAndType_info 类型的常量,表示字段名及描述符
D
1
0x0010 的二进制为 0000 0000 0001 0000,若类是 final 类型,则第 5 位应为 1,否则为 0。
u2
static {
attributes
00
8: astore_2 // 将异常取出,存储到第 3 个变量槽中 {} {this, ?, e}
02
?表 2.2.2-5 CONSTANT_String_info 型常量的结构?
07
1
// Happy.name:Ljava/lang/String;
u2
4 // 将第 5 个变量槽的值复制到操作数栈顶 {e} {this, 3, ?, ?, e}
CONSTANT_InterfaceMethodref_info
00
FE
接下来是方法表(method_info)结构,用于描述方法。
名称
name_index
0D
0E
descriptor_index
u2
枚举类型
name_index
00
数量
[1] 引入泛型后,字段的描述符中泛型的参数化类型被擦除,不能准确描述泛型类型。如 List<String> list 的描述符 descriptor 为 “Ljava/util/List”;
类型
0C
名称
1
数量
05
类型
?表 2.2.5-2 字段表结构?
transient
08
descriptor_index:局部变量的描述符索引
名称
?表 2.2.3-2 访问标志?
local_variable_table_length
2
1
15
数量
描述
// Field java/lang/System.out:Ljava/io/PrintStream;
u2
5
15 ~ 16:0x0018,指向第 24 项常量。
01
tag
类型
2A
1
开始位置(相对于方法体的位置,下同)
1
0A:第 1 项常量的标志位 0x0A,十进制为 10;查表可知这个常量属于 CONSTANT_Methodref_info 类型,代表类中方法的符号引用。
12: istore_3 // 将 2 取出,存储到第 4 个变量槽 {} {this, 2, e, 2}
?表 2.2.2-3 CONSTANT_Methodref_info 常量的结构?
...
1C1 ~ 1C2:0x0002,源码行号 2。
1
java
tag
u2
Happy
// --- finally ---
exception_index_table
10
含义
// String java
0B
33 = Utf8
u2
u2
使用位置
[4] 表面上无参,其实在任何实例方法里,都可以通过 “this” 关键字访问到此方法所属的对象。
1
26 = Class
分别为:魔数、次版本号、主版本号、常量数量、常量表的集合、访问标志、类索引、父类索引、接口索引集合、字段数量、字段表集合、方法数量、方法表集合、属性数量、属性表集合。
?表 2.2.6-3 方法访问标志?
attribute_name_index
09
0A
Code:
LineNumberTable
07
此属性是可选的,可以使用 -g:none 或 -g:source 选项取消这项信息。
ACC_SYNIHETIC
00
1F8 ~ 1F9:0x0006,源码行号 6。
对 class 文件的解析到此结束,下面是从 《深入理解 Java 虚拟机》摘抄的一部分属性表。
ACC_VOLATILE
protected
23 = NameAndType
0A
方法抛出的异常列表
类型
00000160:
?表 2.2.2-2 常量类型?
0F
5: return?码 2.2.7-5 常量赋值?
start_pc
Constant pool:
0x0005
00
名称
00
1C5 ~ 1C6:0x0003,源码行号 3。
1 个字节最多表示 256 条指令。目前,《Java虚拟机规范》已经定义了其中约 200 条编码值对应的指令含义,编码与指令之间的对应关系可查阅《深入理解 Java 虚拟机》的附录 C:“虚拟机字节码指令表”。
整型字面量
06
CONSTANT_Dynamic_info
...
1B1 ~ 1B2:0x0001,Code 的属性数量为 1。
199 ~ 19A:0x0001,局部变量表所需的存储空间(max_locals)。max_locals 的单位是变量槽(Slot),变量槽是 JVM 为局部变量分配内存所使用的最小单位。
00
8
?表 2.2.7-4 LocalVariableTable 属性结构?
09
000001D0:
attribute_length
03
0xB7
为栈顶数据指向的对象的实例变量赋值(显式初始化)
类、方法表、字段表
28
00
u2
u2
u2
00
attribute_length
综合,此字段为 int i;。
00
public int some() throws Exception {
整个 class 文件本质上可以视作是一张表,由所示的数据项按严格顺序排列构成。
不是运行时必须的属性,可以编译时使用 -g:none 或 -g:lines 选项取消这项信息。
将第 1 个变量槽中为引用类型的本地变量推送至栈顶
int
// &34;<init>&34;:()V
01
0E
助记符
01
无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1、2、4、8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码构成字符串值。
0A
Code
1AF ~ 1B0:异常表长度(exception_table_length),0x0000,即方法抛出的异常个数为 0。
描述
final static Integer I = 3;隐式调用 Integer.valueOf(3)方法,不会生成 ConstantValue 属性,在 <clinit> 方法中赋值。
exception_table
access_flags
0x0030 的二进制为 0000 0000 0011 0000,第 5、6 位为 1,代表它是使用 invokespecial 新语义的 final 类,其它位为 0,说明此类型没有 public、abstract 修饰...
含义
00
0x4000
1
4
?图 2.2.4-1 类索引的查找过程?
描述
类文件
?表 2.2.2-4 CONSTANT_Fieldref_info 型常量的结构?
175 ~ 176:字段数量(fields_count),0x0002,类型中声明了 2 个字段。
0C
u2
数量
由于常量池中,常量的数量不固定,需要一项 u2 类型的数据,记录下数量;
7: ireturn // 方法结束,返回操作数栈顶数 1
attributes_count
0F
number_of_exceptions
ACC_PUBLIC
0A
u2
byte
紧接着是 Code 属性的属性;
interfaces
类型
由 final 关键字定义的常量值
1
magic
类型
0x0001
ACC_ENUM
?表 2.2.6-9 LineNumberTable 属性结构?
05
00
类型
Happy.java
类型
}public int some() throws java.lang.Exception;
u2
177 ~ 178:字段访问标志(access_flags),识别字段的修饰符,0x0000。
03
187 ~ 188:方法数量(methods_count),0x0002,类型中声明了 2 个方法。
?码 2.2.2-1 常量表集合?
0x0009,指向第 9 项常量;第 9 项常量属于 CONSTANT_Class_info 类型,它的 name_index 指向第 32 项常量,第 32 项常量属于 CONSTANT_Utf8_info 类型,它的 bytes 经 UTF-8 解码后,为 java/lang/Object。[2]
2A
1
Address
17B ~ 17C:字段的描述符索引(descriptor_index),代表字段的描述符,用来描述字段的数据类型。
// 捕获异常,给 catch 中定义的异常 e 赋值
0
?表 2.2.7-2 SourceFile 属性结构?
调用栈顶引用类型的数据的实例方法
属性长度,不包括开始的 6 个字节
32
to target type
4
attribute_count
line_number
10
00
// Hello World
155:第 38 项常量的标志位 0x01,属于 CONSTANT_Utf8_info 类型,代表 UTF-8 编码的字符串,用来表示字符串字面量、类型的全限定名、字段名、...[3]
ACC_ABSTRACT
1E6 ~ 1E7:异常表长度,0x0000,即方法抛出的异常个数为 0。
attribute_info
69
00000000:
8 Class java/lang/Exception
number_of_exceptions:方法声明抛出的异常的数量
处理位置
00
bootstrap_method_table
name_and_type_index
ACC_STATIC
tag
标志
04 ~ 05:次版本号(Minor Version)0x0000。
ACC_PROTECTED
05
16
?表 2.2.6-7 指令含义?
被声明为 deprecated 的方法和字段
i
1
被模块导出或者开放的包(Package)类和接口的全限定名(Fully Qualified Name)字段的名称和描述符(Descriptor)方法的名称和描述符方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)class 文件中不会保存各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期转换的话是无法得到真正的内存入口地址,也就无法直接被虚拟机使用的。
// x = 3;
EnclosingMethod
// Field name:Ljava/lang/String;
29
标志值
1C7 ~ 1C8:方法的访问标志 0x0009,第 1、4 位为 1,此方法是 public、static 修饰的。
ACC_SYNIHETIC
描述
major_version
引导方法参数数量
int i;
18 = Utf8
()V
0x12
3B
BA
当描述符用来描述方法的参数列表和返回值时,按照先参数列表,后返回值的顺序;如 void some() 的描述符为 ()V,java.lang.String toString() 的描述符为 ()Ljava/lang/String;,void main(String[] args) 的描述符为 ([Ljava/lang/String;)V。
06
00
?表 2.2.3-1 标志?
invokevirtual 指令的参数,指向第 7 项 CONSTANT_Methodref_info 类型的常量:java/io/PrintStream.println:(Ljava/lang/String;)V,代表调用 PrintStream 类的 println(String)方法。
9
u2 类型
u4
3
// 如果 [0,4) 出现 Exception 以外的异常,捕获不了,跳转至 17:
attribute_info
00
1
1
0E
12: putfield
1
ACC_FINAL
00
1B
1
0F
tag
这是 invokespecial 指令的参数,代表一个符号引用,指向第 1 项 CONSTANT_Methodref_info 类型的常量:java/lang/Object.&34;\<init>&34;:()V,说明应调用 Object 的无参构造
09
u2
如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错代码所属的文件名。
u2
内部类列表
0x003D:转十进制为 61,指向第 61 项 CONSTANT_MethodType_info 型常量,代表一个方法类型,(I)V 一个int 类型的形参、返回值类型为 void 的方法。
00
27
u2
1D1 ~ 1D4:0x00000025,属性值长度为 37:1D5 ~ 1F9。
name_and_type_index
attributes_count
Java 代码编译成的字节码指令
attribute_name_index
表示方法类型
attributes
string_index
void
00
u2
...
00
28 = Utf8
00000160:
1
1: istore_1
0xB1
u4
访问常量,直接从常量池中获取值(只包括基本数据类型和字符串字面量,如 CONSTANT_Integer_info、CONSTANT_String_info 等字面量类型)。
constant_pool
u1
00
00
30
内容导视:
访问变量,必须通过指向 CONSTANT_Fieldref_info 的索引,定位到对象地址,再获取此对象的某个字段值。
flags: ACC_PUBLIC
private
000001E0:
00
1EA ~ 1EB:0x0011,属性名称的索引,指向第 17 项常量:“LineNumberTable”。
synchronized
00
boolean
u2
2: iload_1 // 将局部变量表的第 2 个变量槽中的值复制到操作数栈顶 {1} {this, 1}
00
line 1: 0
3: istore_2 // 将 1 取出,存储到第 3 个变量槽中 {} {this, 1, 1}
SourceFile: &34;Happy.java&34;[1] 并不是在方法中用了多少个局部变量,就把这些局部变量所占变量槽数量之和作为 max_locals 的值。
u2
// Method java/lang/Object.&34;<init>&34;:()V
03
stack=2, locals=1, args_size=1
0E
181 ~ 182:0x000C,此字段的字段名索引,指向第 12 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “name”,字段名为 name。
15
数量
注解类型
02
04
14:15
将 int、float、String 型常量值从常量池中复制到操作数栈顶
?表 2.2.5-1 字段表集合?
00
00
16F ~ 170:0x0008 这 2 个字节称为类索引(this_class),用于确定此类的全限定名,指向第 8 项常量。
// 如果 [8, 13) 出现任意异常,转到 17:
08
1
0E
java.lang.String name;
00
// java
名称
25
名称
00
0x4000
02
java/lang/System
类中方法的符号引用
1
iconst_1
CA
0x1000
标志名称
00
06
10: istore_1 // 将 2 取出,存储到第 2 个变量槽 {} {this, 2, e}
15 = Utf8
00
ConstantValue 属性
01
结束位置
0E
0: aload_0
0x003E:转十进制为 62,指向第 62 项 CONSTANT_MethodHandle_info 型常量,REF_invokeStatic com/cqh/arr3/Test.lambda$main$0:(I)V 调用 Test 类型的 lambda... 方法。
ACC_ANNOTATION
0x0004
属性名称
05
18B ~ 18C:0x000E,此方法的方法名索引,指向第 14 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “<init>”。
// Happy
06 ~ 07:主版本号(Major Version)0x0034,对应的十进制为 52,代表 JDK8;JDK1 从 45 开始,每隔一个大版本加 1;高版本的 JDK 可以向下兼容以前版本的 class 文件,反之则不行。
例举方法声明抛出的异常,位于方法表结构的属性表中。
tag
00000150:
ConstantValue
名称
11
数量
02
public void some() {
0x1000
0xB6
0x8000
1
3: ldc
0x0001
描述
u1
61
193 ~ 196:属性值占用的长度(attribute_length),0x00000030,转十进制为 48;从 197 ~ 1C6 都是 Code 属性的内容,这个属性表的长度为 48 + 6 = 54 个字节。
1: istore_1 // 将栈顶 1 从操作数栈取出,存储到局部变量表的第 2 个变量槽 {} {this, 1}
line 3: 9
ldc 指令的参数,代表某字面量;此时推送的是第 3 项常量:“java”
} catch (Exception e) {
标志位 8
u2
// 将异常 e 重新读到操作数栈顶,准备上抛给调用者
01
1F
综上,这段字节码对应的源码应是 System.out.println(&34;Hello World&34;);
00
字段由编译器自动生成
length
1C9 ~ 1CA:方法名索引 0x0012,指向第 18 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “main”。
标志位 9
access_flags
17F ~ 180:0x0000,此字段无修饰符。
0x0003:引导方法参数数量为 3。
// out:Ljava/io/PrintStream;
?码 2.2.1-1 错误信息?
u2
}
[2] 解析阶段的前期绑定与后期绑定。
操作数栈和局部变量表直接决定一个该方法的栈帧所耗费的内存,不必要的操作数栈深度和变量槽数量会造成内存的浪费。
?表 2.2.6-11 一系列字节码指令代表的含义?
[3] code 存储源代码编译后生成的字节码指令。每个字节码指令就是一个 u1 类型的单字节,当虚拟机读取到 code 中的一个字节时,就可以对应找出这个字节代表的是什么指令,并且可以知道这条指令后面是否需要跟随参数,以及后续的参数应当如何解析。
01
14: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, e, 2}
37:38
methods
return
// java/lang/Object.&34;<init>&34;:()V
5
表示一个模块中开放或导出的包
04
u4
19 = Utf8
java/lang/Object
0B ~ 0C:0x0009,指向第 9 项常量,第 9 项常量属于 CONSTANT_Class_info 类型,此常量的 name_index 值为 32,指向第 32 项常量,第 32 常量属于 CONSTANT_Utf8_info 类型,此常量的 bytes 值经 UTF-8 解码后,为 “java/lang/Object”。所以此方法所属类型已然确定。
00
0x0008
名称
08
0x2A
数量
22 = NameAndType
类型
标志位 1
u2
Error: A JNI error has occurred, please check your installation and try again
6E
00
try {
符号引用主要包括下面几类常量:
此索引指向 CONSTANT_Class_info 类型的常量,表示字段所属类型
将第 1 个变量槽中为引用类型的本地变量推送至栈顶
start_pc:局部变量开始的字节码偏移量
ACC_STATIC
02
u2
数量
19F ~ 1AE:存储字节码指令的一系列字节流(code)[3],0x2AB700012A04B500022A1203B50004B1,每个字节码代表的指令:
00
B1
此索引指向 CONSTANT_Utf8_info 类型的常量,表示类或接口的全限定名
00
19: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, ?, ?, ?, e}
08
num_bootstrap_arguments
06
V
08
05
ACC_VARARGS
00
00000170:
attribute_info
1
类型
173 ~ 174:接口数量(interfaces_count),0x0000,转十进制为 0,没有实现任何接口。
例:JDK1 无法执行版本号为 46 及以上的 class 文件。
1: invokespecial 1
05
01
CONSTANT_Utf8_info
方法表
数量
4C
[1] 若是接口类型,父类索引对应的全限定名为 java/lang/Object,接口索引对应的才是此接口继承的父接口全限定名。
表示方法句柄
数量
ACC_NATIVE
...
0B
00
1
05
attribute_name_index
1F2 ~ 1F3:0x0000,字节码行号 0。
2 = Fieldref
E9:0x01,第 31 项常量的标志位,属于 CONSTANT_Utf8_info 类型。
字符串类型字面量
1
attributes 是 attribute 的集合,用来存储一些额外的信息,如 java 代码编译成的字节码指令、final 常量值、方法抛出的异常列表...
protected
exception_info
1
abstract
00
名称
00
07
6
?表 2.2.4-1 类索引、父类索引、接口索引集合?
bootstrap_method_info
00
1C3 ~ 1C4:0x0009,字节码行号 9。
紧接着是 Code 属性的属性;
17 = Utf8
1B9 ~ 1BA:0x0003,行号表长度为 3。
3
01
1
由编译器自动生成
20: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, ?, ?, e}
名称
仅当一个类为局部内部类或匿名类时才能拥有此属性,用于标识这个类所在的外围方法
数量
描述栈帧中局部变量表的变量与源码定义的变量之间的关系,在 Code 属性的属性表中。
CONSTANT_Class_info
类型
00
强行运行会报错,抛出 UnsupportedClassVersionError:
ACC_ENUM
03
getstatic
00
19
此索引指向一个 CONSTANT_Class_info 型的常量
28
num_bootstrap_arguments
int i2 = i1;
0D
00
12 ~ 13:0x0017,指向第 23 项常量。
0D
9.22
0x1000
08
return x;
0x0002
0A
ACC_FINAL
attribute_name_index
u2
1D9 ~ 1DC:0x00000009,存储字节码指令的字节长度为 9。
37 = Utf8
17
abstract
魔数与版本号常量池访问标志类索引、父类索引、接口索引集合字段表集合方法表集合属性表集合java 文件经 javac 编译器编译后生成 class 文件。
u2
15
u2
u2
Exceptions
描述
00
14
u2
start_pc
00
u2 类型
Address
putfield
12 = Utf8
第 3 项常量是字符串字面量 “java”,可表示为 java.lang.String 的实例。
数量
02
1E
aload_0
00
0C
06
u2
189 ~ 18A:方法访问标志,识别方法的修饰符,0x0001,此方法是 public 的。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名都习惯性地以 “_info” 结尾。一系列连续的同一类型、但数量不定的数据称为某类型的集合。
00
扩展:
类型
CONSTANT_InvokeDynamic_info
此索引 CONSTANT_NameAndType_info 类型的常量,表示方法名称及描述符
final
09
// --- finally ---
1
32 = Utf8
19
java/io/PrintStream
由编译器产生的桥接方法
浮点型字面量
含义
名称
33
1
179 ~ 17A:字段名索引(name_index),用于确定字段名;0x000A,指向第 10 项常量;第 10 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 i,所以此字段名为 i。
[2] 如果 field_info 结构表示的非静态字段(如实例变量)包含了 ConstantValue 属性,那么这个属性必须被虚拟机所忽略。
ACC_FINAL
25 = NameAndType
00
sourcefile_index
CONSTANT_MethodHandle_info
?码 2.2.7-2 非字面量形式赋值:调用构造器?
数量
Code 属性结束,此方法结束。
18D ~ 18E:0x000F,此方法的描述符索引,指向第 15 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ()V。
0B
1
0F
00
5 = Fieldref
handler_pc
u4
u2
02
// name:Ljava/lang/String;
1
line_number_table_length
1CF ~ 1D0:0x0010,属性名称的索引,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”。
00
?码 2.2.7-1 声明时不赋值?
每个属性应满足如下结构:
u2
u1
u1
length
u2
u2
0F
07
// i:I
14 = Utf8
02
name_index
length:作用范围覆盖的长度
return x;
04
0E
11
attributes_count
09
CONSTANT_MethodType_info
u2
再回过头来;
1D
若实现了接口,后面每 2 个字节称为一个接口索引,每个接口索引指向某项 CONSTANT_Class_info 类型的常量,对应一个接口的全限定名。
07
1
descriptor: Ljava/lang/String;
long
1
使用十六进制编辑器打开 class 文件,提取一部分:
code_length
因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留第一个变量槽来存放对象实例的引用。
flags: ACC_PUBLIC, ACC_STATIC
00
标志名称
索引指向某项常量
B
表示一个动态方法调用点
interfaces_count
// Happy.i:I
00
04
此索引指向 CONSTANT_Class_info 类型的常量,表示方法所属的类型
方法特征签名最重要的任务就是作为方法独一无二不可重复的 ID。重载(Overload)即方法名相同,但特征签名不同。
descriptor: ()I
?表 2.2.6-6 Code 属性结构?
1
标志位 10
11 = Utf8
特征签名(Signature)多了一项参数化类型的信息: “Ljava/util/List<Ljava/lang/String;>;”。
4
09
02
flags:?表 2.2.6-1 方法表集合?
[1] 一个字节 8 位,每位都是 0 或 1,例 11001010,转为十六进制显示:CA。常常加上前缀表明进制,如 0xCA 代表按十六进制显示的一个字节。
00
num_bootstrap_methods
1
26.27
6E
ldc 指令的参数,代表某字面量,指向第 6 项 CONSTANT_String_info 类型的常量:“Hello World”

36
}0: aload_0
field_info
u2
名称
attribute_name_index
72
Exceptions 属性
接下来是类文件的属性。
00
191 ~ 192:属性名称的索引(attribute_name_index),0x0010,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”,Code 属性是方法体中的代码的字节码描述。
// x = 3;
00
// java/lang/Object
00
B5
类型
08
main
00
13: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, 2, e, 2}
10: ldc
1
I
static
02
CONSTANT_Integer_info
06
01
6E
L
29.30
方法表
exception_index_table 中的每个成员都是对常量池的有效索引,指向 CONSTANT_Class_info 型常量,表示异常的类型。
数量
00
EA ~ EB:0x0005,此字符串字面量占用 5 个字节。(每个英文字符占用 1 个字节)
code
6: putfield
常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量即文本字符串、被声明为 final 的常量值等。
00
u2
0C
对于数组类型,每一维用一个前置的 “[” 字符表示,如 “java.lang.String[][]” 类型的二维数组的描述符为 “[[Ljava/lang/String;”,“int[]” 类型的一维数组的描述符为 “[I”。
00000170:
不超过 32 位的数据类型:byte、char、float、int、short、boolean、returnAddress 等,每个局部变量占用一个变量槽。
00
u2
0A
this version of the Java Runtime only recognizes class file versions up to 52.0意思大概是 class 文件被 JDK16(60.0)编译,使用 JDK8(52.0)运行只能识别 52.0 以下的版本。
长整型字面量
02
u2
数量
00
attribute_count
类型
BE
constant_pool_count
1
16: ireturn // 方法结束,返回 2
cp_info
61
03
line 5: 0
常量池中每一项常量都是一个表,它们都有一个共同的特点,表结构起始的第一位是个 u1 类型的标志位(tag),代表着当前常量属于哪种常量类型。
14:第 3 项常量池的标志位 0x08,属于 CONSTANT_String_info 类型,代表字符串类型的字面量。
类型
156 ~ 157:0x0015,此字符串字面量占用 21 个字节。
字段表
?表 2.2.7-1 属性表集合?
08
06
67
数量
local_variable_table
标志名称
?表 2.2.6-10 line_number_info 结构?
此属性用于保存 invokedynamic 指令引用的引导方法限定符。
}0: iconst_3
09
12
0D ~ 0E:0x0016,十进制为 22,指向第 22 项常量,第 22 项常量属于...略。
minor_version
00
InnerClasses
major version: 52
名称
00
如果当字节码从第 i 行抛出了类型为 catch_type 或其子类的异常,i \in [start_pc,end_pc) ,则转到第 handler_pc 行继续处理;当 catch_type 为 0 时,表示任何异常情况都需要转到 handler_pc 处处理。
ACC_MODULE
// 将 2 重新读到操作数栈顶,准备返回
?码 2.2.7-4 变量赋值?
0A
u1
02
1DD ~ 1E5:0xB200051206B60007B1;
Code:
LineNumberTable:
return
?表 2.2.4-2 CONSTANT_Class_info 型常量的结构?
00
被 final 修饰的字段,在声明时使用字面量的方式赋值,field_info 结构的属性表中会生成此项属性,目的是通知 JVM 自动为类变量赋值。[2]
19B ~ 19E:0x00000010,字节码长度(code_length)为 16。[2]
1
UTF-8 编码的字符串
F
接口类型
24
引导方法表
5: invokevirtual 7
public Happy();
1E2 ~ 1E3:0x0015,源文件名索引(sourcefile_index),指向第 21 项 CONSTANT_Utf8_info 型常量:“Happy.java”;所以源文件名应为 “Happy”。
31
u4
constant_pool_count-1
当虚拟机加载类型时,将会从常量池获得对应的符号引用,再在类创建时或运行时,解析、翻译到具体的内存地址之中。[2]
0x12
0x0080
final class Happy
number_of_exceptions
03
u2
...
ACC_SYNIHETIC
1
?表 2.2.6-2 方法表结构?
10:11
methods_count
bootstrap_method_ref
char
attribute_info
ACC_SYNCHRONIZED
4 // 将异常 e 取出,存储到第 5 个变量槽 {} {this, ?, ?, ?, e}
methods_count
00
02
9: aload_0
0D
74
0x0200
?表 2.2.7-7 BootstrapMethods 属性结构?
31 = Utf8
EC ~ F0:0x4861707079,使用 UTF-8 解码,得 Happy;所以此类的全限定名为 Happy,在默认包下。
0x0035:转十进制为 53,指向第 53 项 CONSTANT_Utf8_info 型常量,bytes 解码为 “BootstrapMethods”。
0x0008
Address
index
1
67
如果不选择生成 LineNumberTable 属性,当抛出异常时,堆栈中将不会显示出错的行号;在调试程序时,也无法按照源码行来设置断点。
01
枚举类型
这是 putfield 指令的参数,代表一个符号引用,指向第 4 项 CONSTANT_Fieldref_info 型常量:Happy.name:Ljava/lang/String;,应为 Happy 类的 String 类型的变量名为 name 的变量赋值
u4
数量
?表 2.2.1-1 魔数与版本号?
u2 类型
00
u4
08
0x0002
00
00
1F6 ~ 1F7:0x0008,字节码行号 8。
// Field i1:I
1
int x;
局部变量的作用域在 [start_pc, start_pc + length) 内。
bootstrap_argument
public
strictfp
描述
Exception table:
ACC_STRICT
07
185 ~ 186:0x0000,此字段没有多余的属性信息。
所以 ()V 代表此方法没有形参,返回值类型为 void。
这个类型并非由用户代码生成
// Field i:I
指向 CONSTANT_MethodHandle_info 型常量
aload_0
num_bootstrap_methods
attribute_length
1
00000020:
02
接下来是第一个引导方法:
u2
exception_table_length
CONSTANT_Long_info
09
x = 3;
12:13
J
descriptor: ()V
// java/io/PrintStream
catch_type
(Ljava/lang/String;)V第 1 项常量是方法的符号引用,代表调用 Object 的 <init> 方法;<init> 是构造方法,内部调用父类构造器(Object 除外)、显示初始化实例变量和执行实例语句块(如果有)、执行本类的构造器中的语句。
volatile
00
17
接下来是字段表(field_info)结构,用于描述字段。
以下为例:
00
0B
BootstrapMethods 属性
34 = Utf8
// 将 2 重新读到操作数栈顶,准备返回
attributes_count
0
助记符
16
final
00
B6
09
00
0B
04
1
1
00
类型
u4
final int i1 = 3;
ACC_PRIVATE
17 any
00
38 = Utf8
34
stack=1, locals=5, args_size=1
“中产阶级”及格线出炉!全中国只有3320万户?你达标了吗?
在这个瞬息万变的时代,我们常常听说“中产阶级”这个词,但真正意义上的中产阶级,你又了解多少呢?当新的标准出炉,宣称全中国只有3320万户能被称为中产,这是否让你心生疑惑:我,又处于其中吗?这不仅是一个经济问题,更是一种生活态度和社会位置的反思。大财经2023-12-28 16:43:500001湖南卫视女主持人 湖南卫视女主播事件
3月1日深夜,前湖南卫视主持人冯禧因一段宣传片被打码一事引起了网友热议,话题冲上热搜后,不少网友都表示疑惑,猜测其中会不会有什么不为人知的故事。据网友爆料,在湖南卫视官方公布的最新一段宣传短片中,片尾出现的冯禧遭到打码处理。而且,通过画面不难发现,冯禧出镜的背景正是她此前与何炅参与主持的综艺节目《你好,星期六》。冯禧被打码的消息曝光之后,迅速引起了网友热议。大财经2023-03-25 09:28:480000epc是什么意思 epc与pc承包模式区别
工程总承包(EPC)模式EPC工程总承包即EngineeringProcurementConstruction模式,又称设计、采购、施工一体化模式。是指在项目决策阶段以后,从设计开始,经招标,委托一家工程公司对设计-采购-建造进行总承包。在这种模式下,按照承包合同规定的总价或可调总价方,由工程公司负责对工程项目的进度、费用、质量、安全进行管理和控制,并按合同约定完成工程。大财经2023-03-22 12:10:100008与中国“抢赛道” 美国政府开始积极规划6G无线通讯
财联社4月24日讯(编辑潇湘)尽管美国眼下都还没完成对5G的全面部署,5G在美国的普及率也非常有限,但华盛顿方面已经开始把目光瞄准向6G了……拜登政府的高级官员上周五主持了一个由美国国家科学基金会主办的活动,开始为即将到来的第六代移动无线通信技术(6G)进行政府规划。0000