十六进制编辑器(手机16进制编辑器)
数量
00
descriptor: Ljava/lang/String;
00
1
// name:Ljava/lang/String;
Exception in thread &34;main&34; java.lang.UnsupportedClassVersionError:
177 ~ 178:字段访问标志(access_flags),识别字段的修饰符,0x0000。
10: istore_1 // 将 2 取出,存储到第 2 个变量槽 {} {this, 2, e}
如果存在异常表,则结构如下:
public int some() throws Exception {
attributes_count
?表 2.2.2-2 常量类型?
line_number_table_length
0x003D:转十进制为 61,指向第 61 项 CONSTANT_MethodType_info 型常量,代表一个方法类型,(I)V 一个int 类型的形参、返回值类型为 void 的方法。
1B5 ~ 1B8:0x0000000E,属性值长度为 14;从 1B9 ~ 1C6 都是此属性的内容。这个属性表的长度为 14 + 6 = 20 个字节,从 1B3 ~ 1C6。
数量
?表 2.2.7-3 Exceptions 属性结构?
00
public
<init>
0B
内部类列表
exception_index_table
interfaces_count
1
ACC_MODULE
08
0xB2
u2
01
21 = Utf8
EA ~ EB:0x0005,此字符串字面量占用 5 个字节。(每个英文字符占用 1 个字节)
此索引指向 CONSTANT_Utf8_info 类型的常量,表示类或接口的全限定名
attributes_count
CONSTANT_InvokeDynamic_info
attributes_count
00
u2
1
0C
u2
0x1000
00
u1
名称
13 = Utf8
紧接着是 Code 属性的属性;
000001B0:
此索引指向 CONSTANT_Class_info 类型的常量,表示方法所属的类型
00
?表 2.2.6-5 不同属性的含义?
76
10
09
Constant pool:
JDK5 引入泛型后,新增属性 LocalVariableTypeTable,仅将 descriptor_index 替换了字段的特征签名(Signature)[1],signature_index 是一个索引,指向 CONSTANT_Utf8_info 型常量。
00
07
code_length
u2
// java
0F
0x000B,指向第 11 项常量,第 11 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 I,所以此字段类型为 int。
30 = NameAndType
u2
attribute_length
Ljava/io/PrintStream;
0C
0x0400
34
0F
1 = Methodref
描述
00
BootstrapMethods 属性
09
name_and_type_index
0B
u4
0x0004
07
...
00
不超过 32 位的数据类型:byte、char、float、int、short、boolean、returnAddress 等,每个局部变量占用一个变量槽。
max_locals
09
14 = Utf8
02
...
6E
0E
0x0010
Code
00
0x0010
access_flags
abstract
flags:?表 2.2.6-1 方法表集合?
int x;
类型
u1
00
?表 2.2.5-2 字段表结构?
00
数量
含义
class 文件是一组以 8 个字节为基础单位的二进制流[1],只有两种数据类型:无符号数、表。
00
24
158 ~ 16C:0x284C6A6176612F6C616E672F537472696E673B2956,经 UTF-8 解码,得 (Ljava/lang/String;)V。
01
此属性用于保存 invokedynamic 指令引用的引导方法限定符。
1C7 ~ 1C8:方法的访问标志 0x0009,第 1、4 位为 1,此方法是 public、static 修饰的。
// Field name:Ljava/lang/String;
1: istore_1
double
第 3 项常量是字符串字面量 “java”,可表示为 java.lang.String 的实例。
CONSTANT_Fieldref_info
26 = Class
接口中的方法的符号引用
引导方法表
minor_version
int i2 = i1;
00
CONSTANT_Utf8_info
23: athrow // 抛出异常
// --- finally ---
0E
1FA ~ 1FB:0x0001,属性个数为 1;
11: iload_1 // 将第 2 个变量槽的值复制到操作数栈顶 {2} {this, 2, e}
5
public
00
0xB7
01
u2
37 = Utf8
19 = Utf8
以下为例:
}
02
00
结束位置
u2
8 = Class
line_number_table
09
// java/io/PrintStream.println:(Ljava/lang/String;)V
数量
0B
ACC_FINAL
0F
07
?表 2.2.6-4 属性结构?
29 = Class
bootstrap_method_table
S
179 ~ 17A:字段名索引(name_index),用于确定字段名;0x000A,指向第 10 项常量;第 10 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 i,所以此字段名为 i。
?表 2.2.7-7 BootstrapMethods 属性结构?
数量
数量
00000160:
V
09
30
18
u4
4 // 将第 5 个变量槽的值复制到操作数栈顶 {e} {this, 3, ?, ?, e}
00
attribute_info
03
1
16F ~ 170:0x0008 这 2 个字节称为类索引(this_class),用于确定此类的全限定名,指向第 8 项常量。
1
...
I
5
?表 2.2.3-1 标志?
?表 2.2.7-1 属性表集合?
0A
数量
CA
bootstrap_method_ref
无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1、2、4、8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码构成字符串值。
// x = 3;
Happy.java
6 = String
0x0007
枚举类型
access_flags
06 ~ 07:主版本号(Major Version)0x0034,对应的十进制为 52,代表 JDK8;JDK1 从 45 开始,每隔一个大版本加 1;高版本的 JDK 可以向下兼容以前版本的 class 文件,反之则不行。
1
1
3: istore_2 // 将 1 取出,存储到第 3 个变量槽中 {} {this, 1, 1}
标志值
00
0E
31
ACC_NATIVE
1
attribute_name_index
08
ACC_ABSTRACT
1AF ~ 1B0:异常表长度(exception_table_length),0x0000,即方法抛出的异常个数为 0。
0x003E:转十进制为 62,指向第 62 项 CONSTANT_MethodHandle_info 型常量,REF_invokeStatic com/cqh/arr3/Test.lambda$main$0:(I)V 调用 Test 类型的 lambda... 方法。
code_length
00
1
5: iconst_1
int
} catch (Exception e) {
// 如果 [0, 4) 出现 Exception 异常,跳转至 8:
表示方法句柄
74
6C
00000000:
4 // 将异常 e 取出,存储到第 5 个变量槽 {} {this, ?, ?, ?, e}
37:38
12
0x04
u2
04
static {
0B
C
00
05
num_bootstrap_methods
?表 2.2.2-6 CONSTANT_Utf8_info 型常量的结构?
当虚拟机加载类型时,将会从常量池获得对应的符号引用,再在类创建时或运行时,解析、翻译到具体的内存地址之中。[2]
u2
17D ~ 17E:属性数量(attributes_count),0x0000,这里为 0,自然就没有属性表了。
3: ldc
?码 2.2.2-1 常量表集合?
05
native
这段字节码代表的含义:调用 Object 的无参构造方法,为 this 指向的对象的 int 类型的实例变量 i 赋值 1,为 this 指向的对象的 String 类型的实例变量 name 赋值 “java”,然后方法结束,返回值为 void。
attribute_length
Address
1B
00
类或接口的符号引用
1CF ~ 1D0:0x0010,属性名称的索引,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”。
8: astore_2 // 将异常取出,存储到第 3 个变量槽中 {} {this, ?, e}
}public int some() throws java.lang.Exception;
ACC_STATIC
由编译器自动生成
05
0D
仅当一个类为局部内部类或匿名类时才能拥有此属性,用于标识这个类所在的外围方法
0xB5
J
// java/lang/System
8
?码 2.2.6-2 方法表集合?
此索引指向 CONSTANT_Utf8_info 类型的常量,表示字符串字面量
char
14
4C
2A
如果不选择生成 LineNumberTable 属性,当抛出异常时,堆栈中将不会显示出错的行号;在调试程序时,也无法按照源码行来设置断点。
0x0800
描述栈帧中局部变量表的变量与源码定义的变量之间的关系,在 Code 属性的属性表中。
8: return
ACC_PRIVATE
ConstantValue
00
attribute_count
attribute_count
00
0F:第 2 项常量的标志位 0x09,十进制为 9,查表可知这个常量属于 CONSTANT_Fieldref_info 类型,代表字段的符号引用。
0F
0A
不是运行时必须的属性,可以编译时使用 -g:none 或 -g:lines 选项取消这项信息。
1
CONSTANT_Package_info
74
u2
1
try {
00
CONSTANT_Double_info
u2
magic
19
// Happy.name:Ljava/lang/String;
0A
此属性是可选的,可以使用 -g:none 或 -g:source 选项取消这项信息。
03
0x4000
CONSTANT_Class_info
02
start_pc
?表 2.2.5-1 字段表集合?
00
u2
含义
1
attributes
02
12: istore_3 // 将 2 取出,存储到第 4 个变量槽 {} {this, 2, e, 2}
00
1
u2
?表 2.2.4-2 CONSTANT_Class_info 型常量的结构?
1
08
表示一个动态方法调用点
1F8 ~ 1F9:0x0006,源码行号 6。
Address
Code:
1
32
attribute_length
0D
10: ldc
表 2.2-1 class 文件结构?
length
flags: ACC_PUBLIC, ACC_STATIC
00
00
调用栈顶数据所指向的对象的实例初始化方法、父类方法、私有方法,后跟一个 u2 类型的参数说明调用哪个方法
09
u2
04 ~ 05:次版本号(Minor Version)0x0000。
0x03
29.30
00
1BF ~ 1C0:0x0004,字节码行号 4。
28 = Utf8
Z
0A
1
private
数量
2: return?表 2.2.7-6 ConstantValue 属性结构?
00000000:
Address
名称
1
助记符
I = 4;
01
从当前方法返回 void
00
u4
volatile
35 = Utf8
19F ~ 1AE:存储字节码指令的一系列字节流(code)[3],0x2AB700012A04B500022A1203B50004B1,每个字节码代表的指令:
u2
助记符
00
descriptor: ()V
this version of the Java Runtime only recognizes class file versions up to 52.0意思大概是 class 文件被 JDK16(60.0)编译,使用 JDK8(52.0)运行只能识别 52.0 以下的版本。
0x0040
类型
length
1
000001E0:
1
将 int、float、String 型常量值从常量池中复制到操作数栈顶
72
1
对于数组类型,每一维用一个前置的 “[” 字符表示,如 “java.lang.String[][]” 类型的二维数组的描述符为 “[[Ljava/lang/String;”,“int[]” 类型的一维数组的描述符为 “[I”。
u2
00
0D
max_stack
1
ACC_ENUM
u2
4
引导方法参数数量
final static int I;
major version: 52
6E
0x0080
// Hello World
// --- catch ---
// x = 3;
start_pc
数量
u4
?表 2.2.6-10 line_number_info 结构?
描述
?表 2.2.7-5 local_variable_table 表结构?
00
53
00000010:
?表 2.2.5-3 字段访问标志?
attributes
17 any若 try 中没有抛出异常,返回 1;若 try 中抛出的异常可被 catch 捕获,跳转至 8:,返回 2;若抛出的异常没有被捕获,跳转至 17,将异常上抛给调用者;无论是否抛出异常,finally 都会执行。
09
1BD ~ 1BE:0x0001,源码行号 1。
12:13
0x2A
名称
sourcefile_index
1
to target type
name_index:局部变量名索引
02
07
1
0A
0F
00000170:
1C3 ~ 1C4:0x0009,字节码行号 9。
01
InnerClasses
?表 2.2.3-2 访问标志?
为栈顶数据指向的对象的实例变量赋值(显式初始化)
含义
// java/lang/Object
Code:
08
short
例:
00000160:
public void some() {
u2
5: invokevirtual 7
this_class
// Field i1:I
// --- finally ---
attribute_name_index
类型
将第 1 个变量槽中为引用类型的本地变量推送至栈顶
u2
描述
descriptor_index
?码 2.2.6-1 异常处理源码及反编译后的指令?
若实现了接口,后面每 2 个字节称为一个接口索引,每个接口索引指向某项 CONSTANT_Class_info 类型的常量,对应一个接口的全限定名。
此索引指向 CONSTANT_Class_info 类型的常量,表示字段所属类型
表示一个模块
ACC_TRANSIENT
00
attribute_length
00
u4
00
181 ~ 182:0x000C,此字段的字段名索引,指向第 12 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “name”,字段名为 name。
CONSTANT_String_info
0x12
使用 javap 反编译工具,加 -verbose 参数输出 class 文件的部分内容:
00
2 个字节能够表示的最大值为 65535,占用字节数最大不能超过 65535,即要小于 64 KB,否则无法通过编译。
1
0E
类型
0B
attribute_info
start_pc
1
1E8 ~ 1E9:0x0001,属性数量为 1。
19: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, ?, ?, ?, e}
06
07
u2
20 = Utf8
05
Error: A JNI error has occurred, please check your installation and try again
综合,此字段为 String name;。
1
6
00
3
1FC ~ 1FD:0x0014,属性名称的索引(attribte_name_index),指向第 20 项 CONSTANT_Utf8_info 类型的常量:“SourceFile”,此属性用于记录生成 class 文件的源码文件名称。
01
1
标志值
22 = NameAndType
local_variable_table_length
1F4 ~ 1F5:0x0005,源码行号 5。
constantvalue_index
类型
// String java
1
0x0010
数量
04
16D ~ 16E:0x0030,访问标志(access_flags),表示类型的修饰符。
// x = 1;对应两个指令
line_number_table_length
u2
x = 3;
0C
00
16
第 4 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 name,java.lang.String 类型。
1DD ~ 1E5:0xB200051206B60007B1;
ldc 指令的参数,代表某字面量,指向第 6 项 CONSTANT_String_info 类型的常量:“Hello World”
00
methods_count
00
由于常量池中,常量的数量不固定,需要一项 u2 类型的数据,记录下数量;
05
类、方法表、字段表
整型字面量
name
这是 putfield 指令的参数,代表一个符号引用,指向第 2 项 CONSTANT_Fieldref_info 型常量:Happy.i:I,应为 Happy 类的 int 类型的变量名为 i 的变量赋值
final int i1 = 3;
访问变量,必须通过指向 CONSTANT_Fieldref_info 的索引,定位到对象地址,再获取此对象的某个字段值。
attributes_count
5: return?码 2.2.7-5 常量赋值?
01
17 any
1F0 ~ 1F1:0x0002,行号表长度为 2。
length
?码 2.2.7-2 非字面量形式赋值:调用构造器?
11
0x2000
注解类型
强行运行会报错,抛出 UnsupportedClassVersionError:
04
0x0020
类型
Happy
13
1
此索引指向 CONSTANT_NameAndType_info 类型的常量,表示字段名及描述符
1
171 ~ 172:0x0009 这两个字节称为父类索引(super_class),用于确定父类的全限定名,除了 java.lang.Object 外,所有类都有父类,索引都不为 0。[1]
08 ~ 09:常量的数量(constant_pool_count),0x0027,十进制为 39,代表常量池中有 38 项常量,索引值为 1 ~ 38。[1]
00
length:作用范围覆盖的长度
ACC_ENUM
06
0x0000 的每位都是 0,此字段没有修饰符。
15 = Utf8
10
ACC_SYNIHETIC
0x0008
start_pc:局部变量开始的字节码偏移量
000001F0:
0B ~ 0C:0x0009,指向第 9 项常量,第 9 项常量属于 CONSTANT_Class_info 类型,此常量的 name_index 值为 32,指向第 32 项常量,第 32 常量属于 CONSTANT_Utf8_info 类型,此常量的 bytes 值经 UTF-8 解码后,为 “java/lang/Object”。所以此方法所属类型已然确定。
描述
00
// --- try ---
00
0x0035:转十进制为 53,指向第 53 项 CONSTANT_Utf8_info 型常量,bytes 解码为 “BootstrapMethods”。
// Happy
0x0008
191 ~ 192:属性名称的索引(attribute_name_index),0x0010,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”,Code 属性是方法体中的代码的字节码描述。
00
0: iconst_1 // 将 int 类型的常量 1 加载到操作数栈顶 {1} {this}
13: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, 2, e, 2}
11 = Utf8
0A
u1 类型
fields_count
...
00000190:
36 = Utf8
1
u2
引导方法的数量
0x0009,指向第 9 项常量;第 9 项常量属于 CONSTANT_Class_info 类型,它的 name_index 指向第 32 项常量,第 32 项常量属于 CONSTANT_Utf8_info 类型,它的 bytes 经 UTF-8 解码后,为 java/lang/Object。[2]
} finally {
I
6: putfield
// 将异常 e 重新读到操作数栈顶,准备上抛给调用者
?表 2.2.7-4 LocalVariableTable 属性结构?
Address
00
attribute_count
这是 invokespecial 指令的参数,代表一个符号引用,指向第 1 项 CONSTANT_Methodref_info 类型的常量:java/lang/Object.&34;\<init>&34;:()V,说明应调用 Object 的无参构造
07
Code
属性长度,不包括开始的 6 个字节
CONSTANT_MethodHandle_info
10 = Utf8
000001A0:
01
?表 2.2.7-2 SourceFile 属性结构?
1
00
?表 2.2.6-11 一系列字节码指令代表的含义?
00
1C9 ~ 1CA:方法名索引 0x0012,指向第 18 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “main”。
1C
6E
00000180:
3 = String
1
Java 代码编译成的字节码指令
例:JDK1 无法执行版本号为 46 及以上的 class 文件。
0D
u2
1
09
bootstrap_method_info
193 ~ 196:属性值占用的长度(attribute_length),0x00000030,转十进制为 48;从 197 ~ 1C6 都是 Code 属性的内容,这个属性表的长度为 48 + 6 = 54 个字节。
2A
0C
1D1 ~ 1D4:0x00000025,属性值长度为 37:1D5 ~ 1F9。
01
10
B6
1F6 ~ 1F7:0x0008,字节码行号 8。
00
2
19
模块
156 ~ 157:0x0015,此字符串字面量占用 21 个字节。
名称
05
名称
类型
04
line 1: 0
u4
1
描述
000001F0:
local_variable_table
}0: aload_0
字节码
?表 2.2.6-7 指令含义?
flags: ACC_PUBLIC
00
名称
(Ljava/lang/String;)V第 1 项常量是方法的符号引用,代表调用 Object 的 <init> 方法;<init> 是构造方法,内部调用父类构造器(Object 除外)、显示初始化实例变量和执行实例语句块(如果有)、执行本类的构造器中的语句。
标志位 7
名称
descriptor: ()I
public static void main(java.lang.String[]);
attribute_name_index
000001C0:
3B
CONSTANT_MethodType_info
185 ~ 186:0x0000,此字段没有多余的属性信息。
07
ACC_VOLATILE
类型
使用 invokespecial 字节码指令的新语义,JDK 1.0.2 后都为真
LineNumberTable
[4] 表面上无参,其实在任何实例方法里,都可以通过 “this” 关键字访问到此方法所属的对象。
使用十六进制编辑器打开 class 文件,提取一部分:
61
?码 2.2.7-4 变量赋值?
在 javac 编译时,把对 this 关键字的访问转变为对一个普通方法参数的访问,在 JVM 调用实例方法时自动传入此参数。
开始位置(相对于方法体的位置,下同)
0A
0x0004
03
[2] 如果 field_info 结构表示的非静态字段(如实例变量)包含了 ConstantValue 属性,那么这个属性必须被虚拟机所忽略。
8
0xB1
attribute_name_index
00
u2
exception_info
ACC_SYNIHETIC
2A
00
08
含义
类型
// {操作数栈最大深度 1} {局部变量表同时生存的局部变量所占最大的槽数 5} {参数个数 1}
02
00
return
183 ~ 184:0x000D,此字段的描述符索引,指向第 13 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Ljava/lang/String;”,此字段的类型为 java.lang.String。
// String Hello World
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名都习惯性地以 “_info” 结尾。一系列连续的同一类型、但数量不定的数据称为某类型的集合。
类中方法的符号引用
00
local_variable_table_length
36
接下来是第 2 个字段;
16: ireturn // 方法结束,返回 2
methods
1A
access_flags
cp_info
CONSTANT_Dynamic_info
指向 CONSTANT_MethodHandle_info 型常量
0C
invokevirtual 指令的参数,指向第 7 项 CONSTANT_Methodref_info 类型的常量:java/io/PrintStream.println:(Ljava/lang/String;)V,代表调用 PrintStream 类的 println(String)方法。
01
1
Address
名称
B5
获取指定类的类变量,并将其值压入操作数栈顶
00
04
06
00
1D9 ~ 1DC:0x00000009,存储字节码指令的字节长度为 9。
15: iload_3 // 将第 4 个变量槽的值复制到操作数栈顶 {2} {this, 3, e, 2}
EnclosingMethod
接下来是类文件的属性。
UTF-8 编码的字符串占用字节数
操作数栈和局部变量表直接决定一个该方法的栈帧所耗费的内存,不必要的操作数栈深度和变量槽数量会造成内存的浪费。
21: aload
被声明为 deprecated 的方法和字段
Exception table:
类型
00
字符串类型字面量
0A
...
00
25 = NameAndType
[2] 虽然它是一个 u4 类型的长度值,理论上最大值可以达到 232,但是《Java虚拟机规范》中明确限制了一个方法不允许超过 65535 条字节码指令,即它实际只使用了 u2 的长度,如果超过这个限制,javac 编译器就会拒绝编译。
00
string_index
6: iload_2 // 将第 3 个变量槽的值复制到操作数栈顶 {1} {this, 3, 1}
float
bytes
09
// x = 2;
UTF-8 编码的字符串
0x0030 的二进制为 0000 0000 0011 0000,第 5、6 位为 1,代表它是使用 invokespecial 新语义的 final 类,其它位为 0,说明此类型没有 public、abstract 修饰...
0x0200
09
06
void
}此字段表不会有 ConstantValue 属性,在 <clinit> 方法中赋值。
name_index
此索引指向一个 CONSTANT_Class_info 型的常量
SourceFile: &34;Happy.java&34;[1] 并不是在方法中用了多少个局部变量,就把这些局部变量所占变量槽数量之和作为 max_locals 的值。
00
数量
14:第 3 项常量池的标志位 0x08,属于 CONSTANT_String_info 类型,代表字符串类型的字面量。
10 ~ 11:0x0008,指向第 8 项常量。
08
Code 属性结束,此方法结束。
constant_pool_count-1
ldc
// 将 2 重新读到操作数栈顶,准备返回
15
SourceFile
?表 2.2.2-4 CONSTANT_Fieldref_info 型常量的结构?
28
u2
0x0036:转十进制为 54,指向第 54 项 CONSTANT_MethodHandle_info 型常量,代表一个方法句柄,REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(...)... 代表调用某静态方法。
对 class 文件的解析到此结束,下面是从 《深入理解 Java 虚拟机》摘抄的一部分属性表。
0x06
u1
10
0x0002
?码 2.2.7-3 非字面量形式赋值:调用方法?
双精度浮点型字面量
02
00
// println:(Ljava/lang/String;)V
00
// Field java/lang/System.out:Ljava/io/PrintStream;
exception_table_length
0F
0x0400
9: iconst_2 // 将常量 2 复制到操作数栈顶 {2} {this, ?, e}
描述
00
u2
03
u2 类型
0x0001 的二进制为 0000 0000 0000 0001,若类是 public 类型,则第 1 位应为 1,否则为 0。
0x4000
000001E0:
0x0080
00
01
u2
字节码
Code:
33 = Utf8
1CD ~ 1CE:0x0001,方法的属性数量为 1。
00000170:
1B1 ~ 1B2:0x0001,Code 的属性数量为 1。
u2
1
0
transient
num_bootstrap_arguments
03
[1] 空出的一项为 0,将索引值设为 0,代表不引用任何一个常量池项目。
1: invokespecial 1
return x;
由 final 关键字定义的常量值
long
对象类型,如 Ljava/lang/Object;
7: ireturn // 方法结束,返回操作数栈顶数 1
07
u2
CONSTANT_Integer_info
?表 2.2.2-3 CONSTANT_Methodref_info 常量的结构?
1
0x0080
1 个字节最多表示 256 条指令。目前,《Java虚拟机规范》已经定义了其中约 200 条编码值对应的指令含义,编码与指令之间的对应关系可查阅《深入理解 Java 虚拟机》的附录 C:“虚拟机字节码指令表”。
00
03
38 = Utf8
1
0xB6
// i:I
25
constantvalue_index:此索引指向常量池中一个字面量常量,根据字段类型不同,可以是 CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、 CONSTANT_Integer_info和CONSTANT_String_info 常量中的一种。
aload_0
u2
// Happy.i:I
B1
fields_count
访问常量,直接从常量池中获取值(只包括基本数据类型和字符串字面量,如 CONSTANT_Integer_info、CONSTANT_String_info 等字面量类型)。
接下来是第一个引导方法:
第 2 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 i,int 类型。
32 = Utf8
17: astore
ACC_INIERFACE
4 = Fieldref
transient
ACC_SYNIHETIC
1
20
u2
// 将 1 重新读到操作数栈顶,准备返回
u4
0B
descriptor_index
27
?表 2.2.7-8 bootstrap_method_table 结构?
abstract
06
u2
02
?表 2.2.4-1 类索引、父类索引、接口索引集合?
stack=2, locals=1, args_size=1
名称
u2
00
()V
0A
03
Ljava/lang/String;
[2] class 文件里,所有的 “.” 都被斜杆 “/” 代替,例 “java.lang.Object” 变为 “java/lang/Object”。
?表 2.2.2-1 常量池?
0x2A
标志名称
名称
方法表
minor version: 0
local_variable_table_length:局部变量个数。
17F ~ 180:0x0000,此字段无修饰符。
u2
// 将 1 重新读到操作数栈顶,准备返回
// 将 2 重新读到操作数栈顶,准备返回
字段由编译器自动生成
code
1
0x1000
catch_type
00
final static String STR = new String(&34;Hello&34;);也不会有 ConstantValue 属性,在 <clinit> 方法中赋值。
u2
descriptor_index
11
再回过头来;
u2
bootstrap_argument
u1
2F
数量
描述
12: putfield
04
00
名称
07
u2
1EA ~ 1EB:0x0011,属性名称的索引,指向第 17 项常量:“LineNumberTable”。
16 = Utf8
name_index
java/io/PrintStream
名称
常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量即文本字符串、被声明为 final 的常量值等。
标志
12
00
...
1
00
attribute_length
?图 2.2.4-1 类索引的查找过程?
B5
CONSTANT_Methodref_info
由编译器产生的桥接方法
descriptor: ([Ljava/lang/String;)V
descriptor_index:局部变量的描述符索引
长整型字面量
为栈顶数据指向的对象的实例变量赋值(显式初始化)
03
数量
08
4: aload_0
public
1
14:15
println
1
?表 2.2.6-9 LineNumberTable 属性结构?
符号引用主要包括下面几类常量:
1
1D7 ~ 1D8:0x0001,局部变量表的存储空间为 1。
17 = Utf8
ACC_PROTECTED
00
attribute_name_index
24 = Utf8
com/cqh/arr3/FieldResolution has been compiled by a more recent version of the Java Runtime (class file version 60.0),
13
15 ~ 16:0x0018,指向第 24 项常量。
6
标志值
将 int 类型的 1 推送至栈顶
line 6: 8
类型
int i1 = 3;
ACC_TRANSIENT
00
tag
[3] 受篇幅原因,其余类型常量的结构,可在 《Java 虚拟机规范》的 4.4 节查看。
189 ~ 18A:方法访问标志,识别方法的修饰符,0x0001,此方法是 public 的。
综上,这段字节码对应的源码应是 System.out.println(&34;Hello World&34;);
09
使用位置
方法表
05
06
当描述符用来描述方法的参数列表和返回值时,按照先参数列表,后返回值的顺序;如 void some() 的描述符为 ()V,java.lang.String toString() 的描述符为 ()Ljava/lang/String;,void main(String[] args) 的描述符为 ([Ljava/lang/String;)V。
number_of_exceptions
handler_pc
浮点型字面量
标志名称
0x0000000C:转十进制为 12,属性剩余长度为 12 个字节。
56
index:局部变量在栈帧的局部变量表中变量槽的位置。当变量数据类型大于 32 位时,它占用的变量槽为 index、index + 1 这两个。
}
如果当字节码从第 i 行抛出了类型为 catch_type 或其子类的异常,i \in [start_pc,end_pc) ,则转到第 handler_pc 行继续处理;当 catch_type 为 0 时,表示任何异常情况都需要转到 handler_pc 处处理。
06
将 int、float 或 String 类型的常量值从常量池中推送至栈顶
如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错代码所属的文件名。
attributes
61
u2
从当前方法返回 void
descriptor: I
所以 ()V 代表此方法没有形参,返回值类型为 void。
tag
protected
number_of_exceptions
0F
1
00
01
1FE ~ 1E1:0x00000002,属性值长度(attribute_length)为 2。
07
数量
00
7 = Methodref
putfield
标志位 9
u2
// 捕获异常,给 catch 中定义的异常 e 赋值
protected
[1] 引入泛型后,字段的描述符中泛型的参数化类型被擦除,不能准确描述泛型类型。如 List<String> list 的描述符 descriptor 为 “Ljava/util/List”;
名称
attribute_name_index
EC ~ F0:0x4861707079,使用 UTF-8 解码,得 Happy;所以此类的全限定名为 Happy,在默认包下。
1
00
name_index
1F
00
名称
8.25
字段或方法的部分符号引用
09
1
1C1 ~ 1C2:0x0002,源码行号 2。
数量
29
return
ACC_ANNOTATION
30
u2
// out:Ljava/io/PrintStream;
表示方法类型
接着是下一个方法表;
67
61
9.22
u2
[1] 若是接口类型,父类索引对应的全限定名为 java/lang/Object,接口索引对应的才是此接口继承的父接口全限定名。
155:第 38 项常量的标志位 0x01,属于 CONSTANT_Utf8_info 类型,代表 UTF-8 编码的字符串,用来表示字符串字面量、类型的全限定名、字段名、...[3]
0F
u2 类型
类型
u2
8.23
29:0x07,十进制为 7,第 8 项常量的标志位,属于 CONSTANT_Class_info 类型。
?码 2.2.1-1 错误信息?
0x0002
魔数与版本号常量池访问标志类索引、父类索引、接口索引集合字段表集合方法表集合属性表集合java 文件经 javac 编译器编译后生成 class 文件。
1
2 = Fieldref
1
invokespecial
0C
invokevirtual
15
0E
09
04
名称
类型
被模块导出或者开放的包(Package)类和接口的全限定名(Fully Qualified Name)字段的名称和描述符(Descriptor)方法的名称和描述符方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)class 文件中不会保存各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期转换的话是无法得到真正的内存入口地址,也就无法直接被虚拟机使用的。
31 = Utf8
02
27 = NameAndType
类型
33
调用栈顶引用类型的数据的实例方法
00
00 ~ 03:0xCAFEBABE,这 4 个字节称为魔数(magic),用来确定此文件是否为一个能被虚拟机接受的 class 文件。[1]
ACC_BRIDGE
attribute_info
// java/io/PrintStream
Address
ACC_PUBLIC
常量池中每一项常量都是一个表,它们都有一个共同的特点,表结构起始的第一位是个 u1 类型的标志位(tag),代表着当前常量属于哪种常量类型。
B
u1
4
0E
00
5 = Fieldref
([Ljava/lang/String;)V
u4
line 3: 9
E9:0x01,第 31 项常量的标志位,属于 CONSTANT_Utf8_info 类型。
LineNumberTable:
00
0x0001:引导方法数量为 1。
ACC_PUBLIC
187 ~ 188:方法数量(methods_count),0x0002,类型中声明了 2 个方法。
00
1B9 ~ 1BA:0x0003,行号表长度为 3。
constant_pool_count
接下来是方法表(method_info)结构,用于描述方法。
LineNumberTable:
被 final 修饰的字段,在声明时使用字面量的方式赋值,field_info 结构的属性表中会生成此项属性,目的是通知 JVM 自动为类变量赋值。[2]
1B3 ~ 1B4:属性名称的索引(attribute_name_index),0x0011,指向第 17 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “LineNumberTable”,此属性描述 Java 源码行号与字节码行号(字节码的偏移量)之间的对应关系。
04
标识字符
u1
exception_table
16
ACC_STATIC
17
00
0x0020
fields
表示一个动态计算常量
0x0005
java.lang.String name;
000001D0:
03
FE
line_number
将局部变量表中的第 1 个变量槽中的引用类型的本地变量推送至栈顶[4]
00
L
4: istore_1
name_index
B1
1
ACC_FINAL
因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留第一个变量槽来存放对象实例的引用。
1
x = 1;
end_pc
attribute_name_index
04
interfaces
01
u2
199 ~ 19A:0x0001,局部变量表所需的存储空间(max_locals)。max_locals 的单位是变量槽(Slot),变量槽是 JVM 为局部变量分配内存所使用的最小单位。
描述
[2] 解析阶段的前期绑定与后期绑定。
[3] code 存储源代码编译后生成的字节码指令。每个字节码指令就是一个 u1 类型的单字节,当虚拟机读取到 code 中的一个字节时,就可以对应找出这个字节代表的是什么指令,并且可以知道这条指令后面是否需要跟随参数,以及后续的参数应当如何解析。
// &34;<init>&34;:()V
stack=2, locals=1, args_size=1
Hello World
BE
num_bootstrap_arguments
20: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, ?, ?, e}
索引指向某项常量
u2
attributes 是 attribute 的集合,用来存储一些额外的信息,如 java 代码编译成的字节码指令、final 常量值、方法抛出的异常列表...
1
ACC_PUBLIC
CONSTANT_Module_info
类型
BA
ldc 指令的参数,代表某字面量;此时推送的是第 3 项常量:“java”
// --- finally ---
0: aload_0
attribute_length
紧接着是 Code 属性的属性;
1EC ~ 1EF:0x0000000A,属性值长度为 10:1EF ~ 1F8。
1F2 ~ 1F3:0x0000,字节码行号 0。
u2
00
00
public void some() {
ACC_ABSTRACT
数量
02
15
标志名称
B2
attributes
1
D
00
flags:
ACC_PROTECTED
Exceptions 属性
名称
CONSTANT_NameAndType_info
69
1
1: getfield
exception_table_length
u2
// 如果 [8, 13) 出现任意异常,转到 17:
Exceptions
attribute_length
CONSTANT_Float_info
197 ~ 198:0x0002,操作数栈(Operand Stack)深度的最大值(max_stack)。JVM 运行的时候需要根据这个值来分配栈帧(Stack Frame)中的操作栈深度。
field_info
6E
00000160:
UTF-8 编码的字符串
?码 2.2.5-1 class 反编译后的字段表内容?
java/lang/Object
0E
from
18D ~ 18E:0x000F,此方法的描述符索引,指向第 15 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ()V。
00
类型
例举方法声明抛出的异常,位于方法表结构的属性表中。
u2
00
26.27
枚举类型
attribute_name_index
0D ~ 0E:0x0016,十进制为 22,指向第 22 项常量,第 22 项常量属于...略。
00
private
1
u2
tag
ACC_STRICT
?表 2.2.2-5 CONSTANT_String_info 型常量的结构?
14: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, e, 2}
1E2 ~ 1E3:0x0015,源文件名索引(sourcefile_index),指向第 21 项 CONSTANT_Utf8_info 型常量:“Happy.java”;所以源文件名应为 “Happy”。
6C
01
类型
0E
u2
28
不是运行必需的属性,可以编译时使用参数 -g:none 或 -g:vars 取消此项信息。
boolean
00
8 Class java/lang/Exception
类型
0x0003:引导方法参数数量为 3。
名称
synchronized
15: return
18 = Utf8
表示一个模块中开放或导出的包
class_index
处理位置
类型
04
?表 2.2.1-1 魔数与版本号?
0x0010 的二进制为 0000 0000 0001 0000,若类是 final 类型,则第 5 位应为 1,否则为 0。
2
CONSTANT_Long_info
接受可变长参数
标志位 8
interfaces_count
u2
java
Deprecated
ACC_VARARGS
整个 class 文件本质上可以视作是一张表,由所示的数据项按严格顺序排列构成。
11
putfield
// java/lang/System.out:Ljava/io/PrintStream;
main
getstatic
ACC_FINAL
ACC_SYNCHRONIZED
00
u2
1
00
0x1000
flags: ACC_PUBLIC
方法抛出的异常列表
ACC_PRIVATE
methods_count
u2
// Method java/lang/Object.&34;<init>&34;:()V
?表 2.2.5-4 描述符字符含义?
5: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, 1}
02
此索引 CONSTANT_NameAndType_info 类型的常量,表示方法名称及描述符
02
18
173 ~ 174:接口数量(interfaces_count),0x0000,转十进制为 0,没有实现任何接口。
局部变量的作用域在 [start_pc, start_pc + length) 内。
0E
描述
u2
0D
类型
1
指向 CONSTANT_Utf8_info 型常量,值为 “BootstrapMethods”
00
1E6 ~ 1E7:异常表长度,0x0000,即方法抛出的异常个数为 0。
final
00
// Field i:I
00
00000020:
// 如果 [0,4) 出现 Exception 以外的异常,捕获不了,跳转至 17:
06
01
00000180:
01
u2
LocalVariableTable 属性
05
// x = 3;
tag
u2 类型
static
方法体里面的代码经过 javac 编译后,最终变为字节码指令存储在 Code 属性内,结构如下:
字段的符号引用
1
0A
0x12
0A
0x0001
final
1
0A:第 1 项常量的标志位 0x0A,十进制为 10;查表可知这个常量属于 CONSTANT_Methodref_info 类型,代表类中方法的符号引用。
类型
01
175 ~ 176:字段数量(fields_count),0x0002,类型中声明了 2 个字段。
方法特征签名最重要的任务就是作为方法独一无二不可重复的 ID。重载(Overload)即方法名相同,但特征签名不同。
00
含义
2A ~ 2B:0x001F,十进制为 31,指向第 31 项常量。
00
08
attribute_info
数量
1
9: aload_0
08
}0: iconst_3
将第 1 个变量槽中为引用类型的本地变量推送至栈顶
06
ldc
9
1
00
08
1BB ~ 1BC:0x0000,字节码行号 0。
17B ~ 17C:字段的描述符索引(descriptor_index),代表字段的描述符,用来描述字段的数据类型。
00
iconst_1
final static Integer I = 3;隐式调用 Integer.valueOf(3)方法,不会生成 ConstantValue 属性,在 <clinit> 方法中赋值。
4: iconst_3 // 将 int 类型的 3 复制到操作数栈顶 {3} {this, 1, 1}
34:35
1CB ~ 1CC:0x0013,此方法的描述符索引,指向第 19 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ([Ljava/lang/String;)V,一个返回值类型为 void,形参为 String[] 类型的方法。
05
02
08
9 = Class
local_variable_info
attributes_count
扩展:
接下来是字段表(field_info)结构,用于描述字段。
18B ~ 18C:0x000E,此方法的方法名索引,指向第 14 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “<init>”。
00
super_class
Address
00
分别为:魔数、次版本号、主版本号、常量数量、常量表的集合、访问标志、类索引、父类索引、接口索引集合、字段数量、字段表集合、方法数量、方法表集合、属性数量、属性表集合。
12
1
接口类型
0x0001
B7
00
u2
67
每个属性应满足如下结构:
0x0002
0x0004
数量
12 ~ 13:0x0017,指向第 23 项常量。
0x003D:转十进制为 61...
?表 2.2.6-8 异常表结构?
含义
u2
class_index
00
4
标志位 10
?码 2.2.7-1 声明时不赋值?
这是 putfield 指令的参数,代表一个符号引用,指向第 4 项 CONSTANT_Fieldref_info 型常量:Happy.name:Ljava/lang/String;,应为 Happy 类的 String 类型的变量名为 name 的变量赋值
1D5 ~ 1D6:0x0002,操作数栈的深度的最大值为 2。
34 = Utf8
attribute_length
0D
09
int i;
00
00000150:
17
7
0xB5
0x0001
08
00
0D
1
line 5: 0
F
u2
0x0100
19B ~ 19E:0x00000010,字节码长度(code_length)为 16。[2]
return x;
数量
exception_index_table 中的每个成员都是对常量池的有效索引,指向 CONSTANT_Class_info 型常量,表示异常的类型。
1
line_number_info
aload_0
stack=1, locals=5, args_size=1
1: istore_1 // 将栈顶 1 从操作数栈取出,存储到局部变量表的第 2 个变量槽 {} {this, 1}
00
03
ConstantValue 属性
00
u4
1
类文件
3
static
major_version
00
// Method java/io/PrintStream.println:(Ljava/lang/String;)V
u2
09
number_of_exceptions:方法声明抛出的异常的数量
double、long 这两种 64 位的数据类型,使用两个变量槽存放。[1]
[1] 使用魔数作为文件格式的标识,因为扩展名可以随意改动,不安全。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过而且不会引起混淆。
u4
1
public Happy();
08
名称
int i2 = i1;
final class Happy
0: getstatic
u1
?表 2.2.6-3 方法访问标志?
00
tag
内容导视:
4
23 = NameAndType
aload_0
?表 2.2.6-2 方法表结构?
u1
method_info
name_and_type_index
数量
1C5 ~ 1C6:0x0003,源码行号 3。
含义
名称
02
6A
u2
Java虚拟机的做法是将局部变量表中的变量槽进行重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占的变量槽可以被其他局部变量所复用,javac 编译器会根据变量的作用域来分配变量槽给各个变量使用,根据同时生存的最大局部变量数量和类型计算出 max_locals 的大小。
1E
0
constant_pool
12
综合,此字段为 int i;。
attribute_length
strictfp
u2
0C
在 Java 代码层面上的方法特征签名只包括方法名称和参数的个数、类型、顺序;而在字节码中的特征签名还包括方法返回值及受检异常表,方法的描述符包括参数列表和返回值。
index
1D
0x0020 的二进制为 0000 0000 0010 0000,若类使用 invokespecial 指令的新语义,则第 6 位应为 1,否则为 0。
20
1
0B
10:11
[1] 一个字节 8 位,每位都是 0 或 1,例 11001010,转为十六进制显示:CA。常常加上前缀表明进制,如 0xCA 代表按十六进制显示的一个字节。
0x8000
1
ACC_SUPER
0D
flags: ACC_FINAL, ACC_SUPER
2F
x = 2;
2: iload_1 // 将局部变量表的第 2 个变量槽中的值复制到操作数栈顶 {1} {this, 1}
java/lang/System
// java/lang/Object.&34;<init>&34;:()V
1
getstatic 指令的参数,代表一个符号引用,指向第 5 项 CONSTANT_Fieldref_info 类型的常量:java/lang/System.out:Ljava/io/PrintStream;,说明获取的是 System 类型的 out 变量
1
01
12 = Utf8
类文件
字段表
out
line 2: 4
0B
属性名称
18F ~ 190:0x0001,方法的属性数量为 1。
69
00
num_bootstrap_methods
byte
0x0040
0x0001
final
1
u1
// --- throw 异常 ---
02
CONSTANT_InterfaceMethodref_info
特征签名(Signature)多了一项参数化类型的信息: “Ljava/util/List<Ljava/lang/String;>;”。
标志位 1
00
名称
0x2A
?表 2.2.6-6 Code 属性结构?
0xB1
u2
影响:所有参数名称都会丢失,IDE 会使用 arg0、arg1 之类的占位符代替原有参数名,在调试期间无法根据参数名称从上下文中获取参数值。
0C
i
01
这个类型并非由用户代码生成
从亏损49w到900w盈利,只因牢记“筹码分布”,避免成为韭菜!
炒股,化繁为简,把复杂的事情简单去做,简单的事情认真去做,大道至简,离成功就近了!曾经有一位表情痛苦不堪的中年人去找柏拉图,他说:“有什么办法可以让我的生活压力少一些?我感觉快喘不过气来了!”柏拉图没有直接回答他的问题,而是给了他一个袋子,然后对他说:“走,我带你到一条石子路,待会儿你把认为漂亮的石头都捡起来装进袋子里。”大财经2023-12-13 14:09:500000林语堂作品 林语堂作品风格特点
在诸多作家中,林语堂,可以说是一位有着卓越成就的作家,他的代表作《京华烟云》,相信朋友们都看过,而且印象之深刻,他不仅在文学上成就斐然,而且他的书法也是值得推崇学习的,其书法水平相当高,并且相当不错,行书酣畅淋漓,笔墨丰腴,而他的草书不拘一格,笔走龙蛇,可以说其书法水平,不输书法家,甚至比书法家写得都精致,下面就让我们一同来欣赏一番林语堂的书法作品吧。大财经2023-03-25 13:10:460000讲述关员自己的故事 | 让老码头焕发新活力
广东佛山,是全国制造业标本城市,“有家就有佛山造”是对佛山制造业最好的诠释。地处佛山西南部的九江镇,位于珠江两大支流——西江、北江的交会处,拥有通江达海的独特地理优势。作为佛山的“南大门”,改革开放45年来,一批批“佛山制造”源源不断从九江口岸出发,远销全球。大财经2023-12-18 15:05:450000国铁集团开始盈利了!你知道吗?
在中国的广袤大地上,纵横交错的铁路网如同一条条巨龙,蜿蜒穿行于城市与乡村之间。这个庞大的铁路帝国,一度背负着沉重的债务负担,然而,就在2023年的年初,它竟然迎来了盈利的曙光。中国铁路的负债高达6万亿,然而,与之形成鲜明对比的是其高达9万多亿的资产。尽管负债率高达60%多,但对于一个重资产、还在大力投资建设的公司来说,这实际上是一个相当健康的状况。大财经2024-01-03 14:46:090000破釜沉舟第一季 破釜沉舟第一季结局
项羽,是在农民起义中涌现出来的一位英雄人物。他是楚国大将项燕的孙子,项燕被秦国所杀,项羽满怀国恨家仇,跟随叔父项梁起兵反秦。陈胜和项梁牺牲之后,项羽成为起义军的核心人物之一。他英勇善战,威名显赫,在歼灭秦军主力、推翻秦王朝过程中建立不朽功勋。司马迁对此充分肯定,按《史记》体例,“本纪”是帝王的传记,项羽没有当过帝王,但司马迁仍然为他写了《项羽本纪》。0000