首页百科大全十六进制编辑器(手机16进制编辑器)
16942

十六进制编辑器(手机16进制编辑器)

大财经2023-03-25 05:58:390

?表 2.2.6-11 一系列字节码指令代表的含义?

此属性用于保存 invokedynamic 指令引用的引导方法限定符。

17

04 ~ 05:次版本号(Minor Version)0x0000。

05

7: ireturn // 方法结束,返回操作数栈顶数 1

u2

将第 1 个变量槽中为引用类型的本地变量推送至栈顶

iconst_1

描述

0x2000

1

// Method java/lang/Object.&34;<init>&34;:()V

00

[1] 若是接口类型,父类索引对应的全限定名为 java/lang/Object,接口索引对应的才是此接口继承的父接口全限定名。

30

在 Java 代码层面上的方法特征签名只包括方法名称和参数的个数、类型、顺序;而在字节码中的特征签名还包括方法返回值及受检异常表,方法的描述符包括参数列表和返回值。

1

0x12

1FA ~ 1FB:0x0001,属性个数为 1;

此索引指向一个 CONSTANT_Class_info 型的常量

综合,此字段为 int i;。

00

[4] 表面上无参,其实在任何实例方法里,都可以通过 “this” 关键字访问到此方法所属的对象。

表示一个动态方法调用点

java

1

接着是下一个方法表;

1

0x0200

1C5 ~ 1C6:0x0003,源码行号 3。

06

u2

[3] 受篇幅原因,其余类型常量的结构,可在 《Java 虚拟机规范》的 4.4 节查看。

如果存在异常表,则结构如下:

u4

03

如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错代码所属的文件名。

14: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, e, 2}

Code:

// x = 3;

访问变量,必须通过指向 CONSTANT_Fieldref_info 的索引,定位到对象地址,再获取此对象的某个字段值。

数量

ACC_SYNCHRONIZED

0C

字段由编译器自动生成

0A

00

数量

综合,此字段为 String name;。

end_pc

// 将 1 重新读到操作数栈顶,准备返回

处理位置

标志位 1

u2

minor version: 0

3: ldc

1

ACC_SYNIHETIC

u2

I = 4;

类型

方法特征签名最重要的任务就是作为方法独一无二不可重复的 ID。重载(Overload)即方法名相同,但特征签名不同。

6

Exception in thread &34;main&34; java.lang.UnsupportedClassVersionError:

?码 2.2.7-4 变量赋值?

attribute_name_index

}

16

类型

length

00

ACC_SUPER

u2

1D7 ~ 1D8:0x0001,局部变量表的存储空间为 1。

对于数组类型,每一维用一个前置的 “[” 字符表示,如 “java.lang.String[][]” 类型的二维数组的描述符为 “[[Ljava/lang/String;”,“int[]” 类型的一维数组的描述符为 “[I”。

LocalVariableTable 属性

ldc 指令的参数,代表某字面量;此时推送的是第 3 项常量:“java”

...

line 1: 0

u2

this_class

UTF-8 编码的字符串

5

CONSTANT_Utf8_info

00

07

1

字段或方法的部分符号引用

00000000:

Exception table:

public void some() {

0C

[1] 一个字节 8 位,每位都是 0 或 1,例 11001010,转为十六进制显示:CA。常常加上前缀表明进制,如 0xCA 代表按十六进制显示的一个字节。

?表 2.2.2-4 CONSTANT_Fieldref_info 型常量的结构?

名称

} catch (Exception e) {

1: invokespecial 1

Code:

类型

字段表

接口中的方法的符号引用

01

00

super_class

CONSTANT_Module_info

由于常量池中,常量的数量不固定,需要一项 u2 类型的数据,记录下数量;

u2

JDK5 引入泛型后,新增属性 LocalVariableTypeTable,仅将 descriptor_index 替换了字段的特征签名(Signature)[1],signature_index 是一个索引,指向 CONSTANT_Utf8_info 型常量。

名称

00

invokespecial

u4

4

0x1000

0D ~ 0E:0x0016,十进制为 22,指向第 22 项常量,第 22 项常量属于...略。

attribute_name_index

0x12

01

stack=1, locals=5, args_size=1

2A ~ 2B:0x001F,十进制为 31,指向第 31 项常量。

0x0040

tag

09

UTF-8 编码的字符串占用字节数

调用栈顶引用类型的数据的实例方法

5: iconst_1

00

float

字节码

32

0B

00

// --- finally ---

0A

00

// java/lang/System

名称

0x0001

u2

04

00

25 = NameAndType

flags:

flags: ACC_FINAL, ACC_SUPER

将 int 类型的 1 推送至栈顶

ldc

数量

0E

21 = Utf8

?码 2.2.5-1 class 反编译后的字段表内容?

189 ~ 18A:方法访问标志,识别方法的修饰符,0x0001,此方法是 public 的。

// --- catch ---

含义

C

4: iconst_3 // 将 int 类型的 3 复制到操作数栈顶 {3} {this, 1, 1}

6E

I

0x0010

1

// out:Ljava/io/PrintStream;

[1] 使用魔数作为文件格式的标识,因为扩展名可以随意改动,不安全。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过而且不会引起混淆。

00

标志位 7

数量

名称

0A

interfaces

18 = Utf8

4

156 ~ 157:0x0015,此字符串字面量占用 21 个字节。

描述

整型字面量

[2] 虽然它是一个 u4 类型的长度值,理论上最大值可以达到 232,但是《Java虚拟机规范》中明确限制了一个方法不允许超过 65535 条字节码指令,即它实际只使用了 u2 的长度,如果超过这个限制,javac 编译器就会拒绝编译。

u2

fields_count

abstract

1

// --- finally ---

exception_table_length

不超过 32 位的数据类型:byte、char、float、int、short、boolean、returnAddress 等,每个局部变量占用一个变量槽。

0D

00000180:

69

bootstrap_method_table

00

179 ~ 17A:字段名索引(name_index),用于确定字段名;0x000A,指向第 10 项常量;第 10 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 i,所以此字段名为 i。

0xB7

被模块导出或者开放的包(Package)类和接口的全限定名(Fully Qualified Name)字段的名称和描述符(Descriptor)方法的名称和描述符方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)class 文件中不会保存各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期转换的话是无法得到真正的内存入口地址,也就无法直接被虚拟机使用的。

u2

魔数与版本号常量池访问标志类索引、父类索引、接口索引集合字段表集合方法表集合属性表集合java 文件经 javac 编译器编译后生成 class 文件。

0F

0A:第 1 项常量的标志位 0x0A,十进制为 10;查表可知这个常量属于 CONSTANT_Methodref_info 类型,代表类中方法的符号引用。

1B1 ~ 1B2:0x0001,Code 的属性数量为 1。

0x0080

public Happy();

00

java/lang/Object

9: iconst_2 // 将常量 2 复制到操作数栈顶 {2} {this, ?, e}

methods

1B3 ~ 1B4:属性名称的索引(attribute_name_index),0x0011,指向第 17 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “LineNumberTable”,此属性描述 Java 源码行号与字节码行号(字节码的偏移量)之间的对应关系。

1C1 ~ 1C2:0x0002,源码行号 2。

00

aload_0

00

attribute_length

length

line_number_table_length

<init>

00

00

类型

00

synchronized

61

此索引指向 CONSTANT_Class_info 类型的常量,表示方法所属的类型

这是 putfield 指令的参数,代表一个符号引用,指向第 4 项 CONSTANT_Fieldref_info 型常量:Happy.name:Ljava/lang/String;,应为 Happy 类的 String 类型的变量名为 name 的变量赋值

number_of_exceptions:方法声明抛出的异常的数量

?表 2.2.5-2 字段表结构?

00

fields_count

标识字符

00

// 将 1 重新读到操作数栈顶,准备返回

class 文件是一组以 8 个字节为基础单位的二进制流[1],只有两种数据类型:无符号数、表。

09

0: iconst_1 // 将 int 类型的常量 1 加载到操作数栈顶 {1} {this}

return

1

com/cqh/arr3/FieldResolution has been compiled by a more recent version of the Java Runtime (class file version 60.0),

18F ~ 190:0x0001,方法的属性数量为 1。

05

methods_count

00000160:

0x0004

line_number_info

interfaces_count

1B5 ~ 1B8:0x0000000E,属性值长度为 14;从 1B9 ~ 1C6 都是此属性的内容。这个属性表的长度为 14 + 6 = 20 个字节,从 1B3 ~ 1C6。

00

fields

00

00

1

local_variable_table_length

J

Ljava/lang/String;

Hello World

02

0A

flags: ACC_PUBLIC, ACC_STATIC

0x0004

如果不选择生成 LineNumberTable 属性,当抛出异常时,堆栈中将不会显示出错的行号;在调试程序时,也无法按照源码行来设置断点。

5: invokevirtual 7

1

含义

00

num_bootstrap_methods

1

为栈顶数据指向的对象的实例变量赋值(显式初始化)

15

1CB ~ 1CC:0x0013,此方法的描述符索引,指向第 19 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ([Ljava/lang/String;)V,一个返回值类型为 void,形参为 String[] 类型的方法。

53

00000190:

01

u2

0C

?码 2.2.7-1 声明时不赋值?

0x0005

CONSTANT_Package_info

17 = Utf8

000001A0:

类文件

15

attribute_length

ConstantValue 属性

14:15

exception_index_table

1 = Methodref

8.23

1

0x0001

描述

开始位置(相对于方法体的位置,下同)

4 // 将第 5 个变量槽的值复制到操作数栈顶 {e} {this, 3, ?, ?, e}

09

1CF ~ 1D0:0x0010,属性名称的索引,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”。

0E

B

使用位置

6: putfield

2: iload_1 // 将局部变量表的第 2 个变量槽中的值复制到操作数栈顶 {1} {this, 1}

start_pc

u2

UTF-8 编码的字符串

u4

I

name

1E6 ~ 1E7:异常表长度,0x0000,即方法抛出的异常个数为 0。

01

0A

ACC_STRICT

?表 2.2.7-8 bootstrap_method_table 结构?

// --- try ---

access_flags

0: getstatic

05

...

指向 CONSTANT_MethodHandle_info 型常量

u4

15 ~ 16:0x0018,指向第 24 项常量。

00

00

属性名称

E9:0x01,第 31 项常量的标志位,属于 CONSTANT_Utf8_info 类型。

调用栈顶数据所指向的对象的实例初始化方法、父类方法、私有方法,后跟一个 u2 类型的参数说明调用哪个方法

0x0001:引导方法数量为 1。

00

u2

int i1 = 3;

// --- finally ---

descriptor: ()I

01

02

putfield

1BD ~ 1BE:0x0001,源码行号 1。

56

04

01

例举方法声明抛出的异常,位于方法表结构的属性表中。

00

int i2 = i1;

1

187 ~ 188:方法数量(methods_count),0x0002,类型中声明了 2 个方法。

u2

u2

bootstrap_method_info

Code:

Address

u1

?表 2.2.6-10 line_number_info 结构?

00

B7

32 = Utf8

attribute_info

02

表示一个模块

u2 类型

标志位 9

15: return

u1

名称

ACC_VARARGS

29 = Class

Code

1A

类型

28

out

00

1

descriptor: Ljava/lang/String;

2F

u2

此索引指向 CONSTANT_Utf8_info 类型的常量,表示字符串字面量

8

...

28 = Utf8

1

00

1

name_index

03

数量

21: aload

handler_pc

0C

0A

u1

11 = Utf8

表示方法类型

0x0008

02

01

6E

// 将异常 e 重新读到操作数栈顶,准备上抛给调用者

00

attributes 是 attribute 的集合,用来存储一些额外的信息,如 java 代码编译成的字节码指令、final 常量值、方法抛出的异常列表...

1

static

descriptor_index

符号引用主要包括下面几类常量:

line_number_table_length

标志名称

1F2 ~ 1F3:0x0000,字节码行号 0。

1F8 ~ 1F9:0x0006,源码行号 6。

25

名称

标志名称

exception_table_length

1B

exception_info

数量

05

分别为:魔数、次版本号、主版本号、常量数量、常量表的集合、访问标志、类索引、父类索引、接口索引集合、字段数量、字段表集合、方法数量、方法表集合、属性数量、属性表集合。

num_bootstrap_arguments

00

int

27

attributes_count

00 ~ 03:0xCAFEBABE,这 4 个字节称为魔数(magic),用来确定此文件是否为一个能被虚拟机接受的 class 文件。[1]

名称

ACC_SYNIHETIC

08

ACC_PROTECTED

所以 ()V 代表此方法没有形参,返回值类型为 void。

00

再回过头来;

02

00

int i2 = i1;

类型

u2

00

1

类型

public int some() throws Exception {

4 = Fieldref

数量

06

00000150:

00

EnclosingMethod

3

1

09

CONSTANT_Methodref_info

// Field i:I

描述

数量

attribute_length

枚举类型

1

16 = Utf8

00

29.30

u2

B5

内部类列表

名称

00

00

名称

00

含义

0x0100

1

1

1

1

// Hello World

00

24 = Utf8

05

0A

00

u4

ACC_PROTECTED

ACC_ENUM

1EA ~ 1EB:0x0011,属性名称的索引,指向第 17 项常量:“LineNumberTable”。

attributes_count

20

2 个字节能够表示的最大值为 65535,占用字节数最大不能超过 65535,即要小于 64 KB,否则无法通过编译。

00

1

1F4 ~ 1F5:0x0005,源码行号 5。

13: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, 2, e, 2}

类或接口的符号引用

?表 2.2.2-1 常量池?

00

方法抛出的异常列表

[3] code 存储源代码编译后生成的字节码指令。每个字节码指令就是一个 u1 类型的单字节,当虚拟机读取到 code 中的一个字节时,就可以对应找出这个字节代表的是什么指令,并且可以知道这条指令后面是否需要跟随参数,以及后续的参数应当如何解析。

1

1FC ~ 1FD:0x0014,属性名称的索引(attribte_name_index),指向第 20 项 CONSTANT_Utf8_info 类型的常量:“SourceFile”,此属性用于记录生成 class 文件的源码文件名称。

00

09

12

00

flags: ACC_PUBLIC

u2

?表 2.2.6-6 Code 属性结构?

34:35

07

i

tag

name_and_type_index

ACC_ABSTRACT

0F

catch_type

00

02

num_bootstrap_arguments

00

000001C0:

表示一个动态计算常量

19

191 ~ 192:属性名称的索引(attribute_name_index),0x0010,指向第 16 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Code”,Code 属性是方法体中的代码的字节码描述。

line_number

0D

stack=2, locals=1, args_size=1

// java/io/PrintStream

u2

0

string_index

2F

09

line 5: 0

08

double

u2

major_version

1B9 ~ 1BA:0x0003,行号表长度为 3。

00

println

描述栈帧中局部变量表的变量与源码定义的变量之间的关系,在 Code 属性的属性表中。

u2

// String Hello World

局部变量的作用域在 [start_pc, start_pc + length) 内。

u2

6 = String

注解类型

0x0400

1

BE

u2

1

02

exception_table

int x;

CONSTANT_Integer_info

08

1

access_flags

158 ~ 16C:0x284C6A6176612F6C616E672F537472696E673B2956,经 UTF-8 解码,得 (Ljava/lang/String;)V。

04

8: astore_2 // 将异常取出,存储到第 3 个变量槽中 {} {this, ?, e}

tag

EA ~ EB:0x0005,此字符串字面量占用 5 个字节。(每个英文字符占用 1 个字节)

175 ~ 176:字段数量(fields_count),0x0002,类型中声明了 2 个字段。

每个属性应满足如下结构:

?表 2.2.5-4 描述符字符含义?

名称

?表 2.2.7-7 BootstrapMethods 属性结构?

类型

CONSTANT_Float_info

此索引指向 CONSTANT_Class_info 类型的常量,表示字段所属类型

183 ~ 184:0x000D,此字段的描述符索引,指向第 13 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “Ljava/lang/String;”,此字段的类型为 java.lang.String。

local_variable_table_length:局部变量个数。

start_pc:局部变量开始的字节码偏移量

00

public

0x0001 的二进制为 0000 0000 0000 0001,若类是 public 类型,则第 1 位应为 1,否则为 0。

1FE ~ 1E1:0x00000002,属性值长度(attribute_length)为 2。

67

2: return?表 2.2.7-6 ConstantValue 属性结构?

1

java/io/PrintStream

8 = Class

08

表 2.2-1 class 文件结构?

26 = Class

CONSTANT_Fieldref_info

19F ~ 1AE:存储字节码指令的一系列字节流(code)[3],0x2AB700012A04B500022A1203B50004B1,每个字节码代表的指令:

length

}public int some() throws java.lang.Exception;

26.27

长整型字面量

数量

23: athrow // 抛出异常

ACC_PUBLIC

000001F0:

接下来是字段表(field_info)结构,用于描述字段。

1E8 ~ 1E9:0x0001,属性数量为 1。

0F

ConstantValue

类型

数量

u2

08

u2

public void some() {

CONSTANT_MethodHandle_info

类型

常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量即文本字符串、被声明为 final 的常量值等。

00

被 final 修饰的字段,在声明时使用字面量的方式赋值,field_info 结构的属性表中会生成此项属性,目的是通知 JVM 自动为类变量赋值。[2]

0xB5

SourceFile

} finally {

?表 2.2.6-2 方法表结构?

04

12

方法体里面的代码经过 javac 编译后,最终变为字节码指令存储在 Code 属性内,结构如下:

?表 2.2.2-5 CONSTANT_String_info 型常量的结构?

04

05

00

main

?表 2.2.7-2 SourceFile 属性结构?

00

1E

CA

// Happy.i:I

30 = NameAndType

00

这个类型并非由用户代码生成

1BB ~ 1BC:0x0000,字节码行号 0。

1

8: return

0E

bootstrap_argument

索引指向某项常量

[2] 解析阶段的前期绑定与后期绑定。

05

17D ~ 17E:属性数量(attributes_count),0x0000,这里为 0,自然就没有属性表了。

00

Address

13

0xB1

code_length

17 any若 try 中没有抛出异常,返回 1;若 try 中抛出的异常可被 catch 捕获,跳转至 8:,返回 2;若抛出的异常没有被捕获,跳转至 17,将异常上抛给调用者;无论是否抛出异常,finally 都会执行。

descriptor_index:局部变量的描述符索引

[2] 如果 field_info 结构表示的非静态字段(如实例变量)包含了 ConstantValue 属性,那么这个属性必须被虚拟机所忽略。

attributes

u1

0x003E:转十进制为 62,指向第 62 项 CONSTANT_MethodHandle_info 型常量,REF_invokeStatic com/cqh/arr3/Test.lambda$main$0:(I)V 调用 Test 类型的 lambda... 方法。

13 = Utf8

u2 类型

Z

描述

ACC_FINAL

14 = Utf8

方法表

attributes_count

00

?表 2.2.2-2 常量类型?

name_index

ACC_PUBLIC

u2

0B

u1

cp_info

00

Code

185 ~ 186:0x0000,此字段没有多余的属性信息。

attribute_length

ACC_SYNIHETIC

标志

01

final static String STR = new String(&34;Hello&34;);也不会有 ConstantValue 属性,在 <clinit> 方法中赋值。

01

3B

紧接着是 Code 属性的属性;

12 ~ 13:0x0017,指向第 23 项常量。

05

00

1

Ljava/io/PrintStream;

由 final 关键字定义的常量值

0x0009,指向第 9 项常量;第 9 项常量属于 CONSTANT_Class_info 类型,它的 name_index 指向第 32 项常量,第 32 项常量属于 CONSTANT_Utf8_info 类型,它的 bytes 经 UTF-8 解码后,为 java/lang/Object。[2]

0C

Error: A JNI error has occurred, please check your installation and try again

例:JDK1 无法执行版本号为 46 及以上的 class 文件。

CONSTANT_String_info

接下来是类文件的属性。

u2

方法表

表示方法句柄

tag

?码 2.2.2-1 常量表集合?

08 ~ 09:常量的数量(constant_pool_count),0x0027,十进制为 39,代表常量池中有 38 项常量,索引值为 1 ~ 38。[1]

attribute_name_index

1F

17B ~ 17C:字段的描述符索引(descriptor_index),代表字段的描述符,用来描述字段的数据类型。

u2

1D5 ~ 1D6:0x0002,操作数栈的深度的最大值为 2。

1

31 = Utf8

attribute_length

?码 2.2.7-3 非字面量形式赋值:调用方法?

descriptor_index

Constant pool:

1

描述

38 = Utf8

final

5

字段的符号引用

2A

16F ~ 170:0x0008 这 2 个字节称为类索引(this_class),用于确定此类的全限定名,指向第 8 项常量。

这段字节码代表的含义:调用 Object 的无参构造方法,为 this 指向的对象的 int 类型的实例变量 i 赋值 1,为 this 指向的对象的 String 类型的实例变量 name 赋值 “java”,然后方法结束,返回值为 void。

0x0002

?表 2.2.4-2 CONSTANT_Class_info 型常量的结构?

00

max_stack

17: astore

01

u2

00

public

因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留第一个变量槽来存放对象实例的引用。

11

01

index

line 3: 9

描述

SourceFile: &34;Happy.java&34;[1] 并不是在方法中用了多少个局部变量,就把这些局部变量所占变量槽数量之和作为 max_locals 的值。

将第 1 个变量槽中为引用类型的本地变量推送至栈顶

29

16

1

0x0002

类中方法的符号引用

return x;

?表 2.2.6-4 属性结构?

BootstrapMethods 属性

将局部变量表中的第 1 个变量槽中的引用类型的本地变量推送至栈顶[4]

02

由编译器产生的桥接方法

02

?码 2.2.7-2 非字面量形式赋值:调用构造器?

// x = 2;

u1 类型

Address

10

00

10 ~ 11:0x0008,指向第 8 项常量。

00

类型

数量

00

18

29:0x07,十进制为 7,第 8 项常量的标志位,属于 CONSTANT_Class_info 类型。

4: aload_0

1

u2

int i;

Address

0x0010

CONSTANT_MethodType_info

将 int、float、String 型常量值从常量池中复制到操作数栈顶

static {

ACC_FINAL

u2 类型

属性长度,不包括开始的 6 个字节

6E

33 = Utf8

// Happy.name:Ljava/lang/String;

0B ~ 0C:0x0009,指向第 9 项常量,第 9 项常量属于 CONSTANT_Class_info 类型,此常量的 name_index 值为 32,指向第 32 项常量,第 32 常量属于 CONSTANT_Utf8_info 类型,此常量的 bytes 值经 UTF-8 解码后,为 “java/lang/Object”。所以此方法所属类型已然确定。

17F ~ 180:0x0000,此字段无修饰符。

0E

00

1

33

类型

引导方法表

03

1C3 ~ 1C4:0x0009,字节码行号 9。

u2

0F:第 2 项常量的标志位 0x09,十进制为 9,查表可知这个常量属于 CONSTANT_Fieldref_info 类型,代表字段的符号引用。

}0: iconst_3

0F

从当前方法返回 void

数量

12

00000010:

第 2 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 i,int 类型。

0x04

13

00000020:

00

1EC ~ 1EF:0x0000000A,属性值长度为 10:1EF ~ 1F8。

attribute_info

69

return x;

01

1F6 ~ 1F7:0x0008,字节码行号 8。

5 = Fieldref

02

u2

19

field_info

使用 invokespecial 字节码指令的新语义,JDK 1.0.2 后都为真

在 javac 编译时,把对 this 关键字的访问转变为对一个普通方法参数的访问,在 JVM 调用实例方法时自动传入此参数。

00

final

2

attribute_length

protected

10: istore_1 // 将 2 取出,存储到第 2 个变量槽 {} {this, 2, e}

4C

1

10: ldc

constantvalue_index:此索引指向常量池中一个字面量常量,根据字段类型不同,可以是 CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、 CONSTANT_Integer_info和CONSTANT_String_info 常量中的一种。

00

00

u2

15 = Utf8

引导方法的数量

?图 2.2.4-1 类索引的查找过程?

04

1

0D

00000160:

接受可变长参数

ldc

constant_pool_count-1

name_index

0B

标志名称

0x0020 的二进制为 0000 0000 0010 0000,若类使用 invokespecial 指令的新语义,则第 6 位应为 1,否则为 0。

constantvalue_index

00

从当前方法返回 void

00

num_bootstrap_methods

protected

09

00

1

CONSTANT_Dynamic_info

例:

// 将 2 重新读到操作数栈顶,准备返回

12: istore_3 // 将 2 取出,存储到第 4 个变量槽 {} {this, 2, e, 2}

04

?表 2.2.4-1 类索引、父类索引、接口索引集合?

1

07

u2

类型

1

0D

浮点型字面量

综上,这段字节码对应的源码应是 System.out.println(&34;Hello World&34;);

4 // 将异常 e 取出,存储到第 5 个变量槽 {} {this, ?, ?, ?, e}

00

第 4 项常量是字段的符号引用,说明此字段在 Happy 类中,变量名为 name,java.lang.String 类型。

000001D0:

CONSTANT_NameAndType_info

BA

// 捕获异常,给 catch 中定义的异常 e 赋值

34 = Utf8

1

final int i1 = 3;

ACC_MODULE

09

S

CONSTANT_Class_info

?表 2.2.7-4 LocalVariableTable 属性结构?

23 = NameAndType

?码 2.2.1-1 错误信息?

// java/lang/System.out:Ljava/io/PrintStream;

descriptor: ()V

name_and_type_index

attribute_name_index

枚举类型

?表 2.2.7-3 Exceptions 属性结构?

名称

09

class_index

1C9 ~ 1CA:方法名索引 0x0012,指向第 18 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “main”。

try {

1DD ~ 1E5:0xB200051206B60007B1;

u4

?表 2.2.7-1 属性表集合?

// 如果 [0,4) 出现 Exception 以外的异常,捕获不了,跳转至 17:

00

ACC_TRANSIENT

4: istore_1

sourcefile_index

07

u2

11

u2

被声明为 deprecated 的方法和字段

line 6: 8

1

([Ljava/lang/String;)V

类、方法表、字段表

1C7 ~ 1C8:方法的访问标志 0x0009,第 1、4 位为 1,此方法是 public、static 修饰的。

0D

}0: aload_0

01

line 2: 4

method_info

访问常量,直接从常量池中获取值(只包括基本数据类型和字符串字面量,如 CONSTANT_Integer_info、CONSTANT_String_info 等字面量类型)。

return

000001E0:

0x0400

接下来是第一个引导方法:

18

00

Happy.java

00

类型

u2

native

final static int I;

u2

1

0A

line_number_table

1

InnerClasses

attribute_length

表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名都习惯性地以 “_info” 结尾。一系列连续的同一类型、但数量不定的数据称为某类型的集合。

0E

magic

00

0x0035:转十进制为 53,指向第 53 项 CONSTANT_Utf8_info 型常量,bytes 解码为 “BootstrapMethods”。

0E

当描述符用来描述方法的参数列表和返回值时,按照先参数列表,后返回值的顺序;如 void some() 的描述符为 ()V,java.lang.String toString() 的描述符为 ()Ljava/lang/String;,void main(String[] args) 的描述符为 ([Ljava/lang/String;)V。

标志值

u2

// Happy

类型

u2

this version of the Java Runtime only recognizes class file versions up to 52.0意思大概是 class 文件被 JDK16(60.0)编译,使用 JDK8(52.0)运行只能识别 52.0 以下的版本。

u2

03

u2

0x2A

Java 代码编译成的字节码指令

0x4000

扩展:

?表 2.2.7-5 local_variable_table 表结构?

start_pc

标志位 10

0xB2

0x4000

0x003D:转十进制为 61,指向第 61 项 CONSTANT_MethodType_info 型常量,代表一个方法类型,(I)V 一个int 类型的形参、返回值类型为 void 的方法。

14

00

名称

00

aload_0

00

0x000B,指向第 11 项常量,第 11 项常量是 CONSTANT_Utf8_info 类型,它的 bytes 经解码后为 I,所以此字段类型为 int。

ACC_INIERFACE

00

u2

0x8000

class_index

0D

00

u2

00

ACC_ABSTRACT

000001E0:

00

07

3 = String

constant_pool_count

18B ~ 18C:0x000E,此方法的方法名索引,指向第 14 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “<init>”。

Address

0x0020

?表 2.2.6-9 LineNumberTable 属性结构?

x = 2;

35 = Utf8

30

类型

from

code

u2

模块

6: iload_2 // 将第 3 个变量槽的值复制到操作数栈顶 {1} {this, 3, 1}

u2

local_variable_table

6

01

0F

8.25

08

6E

attribute_count

0D

类型

Address

descriptor: I

使用十六进制编辑器打开 class 文件,提取一部分:

0x0080

含义

bytes

05

// 将 2 重新读到操作数栈顶,准备返回

03

attribute_name_index

61

1

1

V

// Field name:Ljava/lang/String;

u2

}

1: istore_1 // 将栈顶 1 从操作数栈取出,存储到局部变量表的第 2 个变量槽 {} {this, 1}

02

getstatic 指令的参数,代表一个符号引用,指向第 5 项 CONSTANT_Fieldref_info 类型的常量:java/lang/System.out:Ljava/io/PrintStream;,说明获取的是 System 类型的 out 变量

...

// 如果 [0, 4) 出现 Exception 异常,跳转至 8:

u2

09

74

local_variable_info

u2

1

1

03

ACC_PUBLIC

00

// name:Ljava/lang/String;

1

数量

?表 2.2.3-1 标志?

7 = Methodref

数量

1BF ~ 1C0:0x0004,字节码行号 4。

...

由编译器自动生成

名称

19B ~ 19E:0x00000010,字节码长度(code_length)为 16。[2]

常量池中每一项常量都是一个表,它们都有一个共同的特点,表结构起始的第一位是个 u1 类型的标志位(tag),代表着当前常量属于哪种常量类型。

?表 2.2.6-3 方法访问标志?

code_length

36

// java/lang/Object

名称

03

24

含义

描述

1

指向 CONSTANT_Utf8_info 型常量,值为 “BootstrapMethods”

number_of_exceptions

00

07

0x0010

00

0x03

00

1

表示一个模块中开放或导出的包

1C

00

EC ~ F0:0x4861707079,使用 UTF-8 解码,得 Happy;所以此类的全限定名为 Happy,在默认包下。

attributes

类型

CONSTANT_Long_info

00000180:

putfield

09

1D

此索引指向 CONSTANT_Utf8_info 类型的常量,表示类或接口的全限定名

u2

含义

12:13

9

java/lang/System

ACC_PRIVATE

1

// --- throw 异常 ---

B1

// println:(Ljava/lang/String;)V

double、long 这两种 64 位的数据类型,使用两个变量槽存放。[1]

0x0010 的二进制为 0000 0000 0001 0000,若类是 final 类型,则第 5 位应为 1,否则为 0。

0A

整个 class 文件本质上可以视作是一张表,由所示的数据项按严格顺序排列构成。

u1

此属性是可选的,可以使用 -g:none 或 -g:source 选项取消这项信息。

1D9 ~ 1DC:0x00000009,存储字节码指令的字节长度为 9。

27 = NameAndType

不是运行时必须的属性,可以编译时使用 -g:none 或 -g:lines 选项取消这项信息。

08

20

1: getfield

// String java

6A

00

0B

u4

minor_version

00

03

1

若实现了接口,后面每 2 个字节称为一个接口索引,每个接口索引指向某项 CONSTANT_Class_info 类型的常量,对应一个接口的全限定名。

Code 属性结束,此方法结束。

76

00

10

为栈顶数据指向的对象的实例变量赋值(显式初始化)

02

number_of_exceptions

01

08

4

abstract

u2

22 = NameAndType

08

...

标志位 8

00

LineNumberTable:

CONSTANT_InterfaceMethodref_info

u4

0x0080

标志值

attribute_name_index

// java

ACC_ENUM

09

0x2A

对象类型,如 Ljava/lang/Object;

173 ~ 174:接口数量(interfaces_count),0x0000,转十进制为 0,没有实现任何接口。

数量

36 = Utf8

// {操作数栈最大深度 1} {局部变量表同时生存的局部变量所占最大的槽数 5} {参数个数 1}

6C

attribute_info

attributes

34

1

ACC_NATIVE

197 ~ 198:0x0002,操作数栈(Operand Stack)深度的最大值(max_stack)。JVM 运行的时候需要根据这个值来分配栈帧(Stack Frame)中的操作栈深度。

?表 2.2.2-6 CONSTANT_Utf8_info 型常量的结构?

byte

u1

1CD ~ 1CE:0x0001,方法的属性数量为 1。

1D1 ~ 1D4:0x00000025,属性值长度为 37:1D5 ~ 1F9。

紧接着是 Code 属性的属性;

02

2A

getstatic

06

14:第 3 项常量池的标志位 0x08,属于 CONSTANT_String_info 类型,代表字符串类型的字面量。

0x0000000C:转十进制为 12,属性剩余长度为 12 个字节。

11: iload_1 // 将第 2 个变量槽的值复制到操作数栈顶 {2} {this, 2, e}

attributes_count

0xB6

?表 2.2.5-3 字段访问标志?

private

1: istore_1

数量

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

// Method java/io/PrintStream.println:(Ljava/lang/String;)V

数量

methods_count

// Field i1:I

descriptor_index

0A

08

0C

0B

06

[1] 空出的一项为 0,将索引值设为 0,代表不引用任何一个常量池项目。

00

0B

ACC_PRIVATE

10:11

Exceptions

0x003D:转十进制为 61...

0x0008

00000000:

B2

ACC_STATIC

00

19 = Utf8

local_variable_table_length

07

20: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, ?, ?, e}

access_flags

attribute_count

助记符

0C

1

attribute_length

16: ireturn // 方法结束,返回 2

12

类型

Exceptions 属性

name_index

// &34;<init>&34;:()V

0xB5

B6

18D ~ 18E:0x000F,此方法的描述符索引,指向第 15 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 ()V。

0F

此索引指向 CONSTANT_NameAndType_info 类型的常量,表示字段名及描述符

0E

10

0

1AF ~ 1B0:异常表长度(exception_table_length),0x0000,即方法抛出的异常个数为 0。

D

67

java.lang.String name;

final class Happy

void

transient

// x = 3;

1

0x0004

?表 2.2.2-3 CONSTANT_Methodref_info 常量的结构?

6C

74

17 any

attribute_length

00

00

数量

Deprecated

0B

(Ljava/lang/String;)V第 1 项常量是方法的符号引用,代表调用 Object 的 <init> 方法;<init> 是构造方法,内部调用父类构造器(Object 除外)、显示初始化实例变量和执行实例语句块(如果有)、执行本类的构造器中的语句。

名称

4

12: putfield

bootstrap_method_ref

0x0000 的每位都是 0,此字段没有修饰符。

u2

?表 2.2.6-7 指令含义?

十六进制编辑器 手机16进制编辑器

u2

stack=2, locals=1, args_size=1

ACC_FINAL

8

0x0020

72

max_locals

0x1000

000001B0:

1

0x2A

07

37:38

字符串类型字面量

attributes

181 ~ 182:0x000C,此字段的字段名索引,指向第 12 项 CONSTANT_Utf8_info 类型的常量,此常量的 bytes 经解码后为 “name”,字段名为 name。

FE

如果当字节码从第 i 行抛出了类型为 catch_type 或其子类的异常,i \in [start_pc,end_pc) ,则转到第 handler_pc 行继续处理;当 catch_type 为 0 时,表示任何异常情况都需要转到 handler_pc 处处理。

37 = Utf8

u4

15: iload_3 // 将第 4 个变量槽的值复制到操作数栈顶 {2} {this, 3, e, 2}

171 ~ 172:0x0009 这两个字节称为父类索引(super_class),用于确定父类的全限定名,除了 java.lang.Object 外,所有类都有父类,索引都不为 0。[1]

08

exception_index_table 中的每个成员都是对常量池的有效索引,指向 CONSTANT_Class_info 型常量,表示异常的类型。

tag

15

major version: 52

1

[1] 引入泛型后,字段的描述符中泛型的参数化类型被擦除,不能准确描述泛型类型。如 List<String> list 的描述符 descriptor 为 “Ljava/util/List”;

00

0x0800

ACC_STATIC

Happy

61

0x0001

00

u1

0x0030 的二进制为 0000 0000 0011 0000,第 5、6 位为 1,代表它是使用 invokespecial 新语义的 final 类,其它位为 0,说明此类型没有 public、abstract 修饰...

00

00

03

?码 2.2.6-2 方法表集合?

final static Integer I = 3;隐式调用 Integer.valueOf(3)方法,不会生成 ConstantValue 属性,在 <clinit> 方法中赋值。

类文件

第 3 项常量是字符串字面量 “java”,可表示为 java.lang.String 的实例。

将 int、float 或 String 类型的常量值从常量池中推送至栈顶

3: istore_2 // 将 1 取出,存储到第 3 个变量槽中 {} {this, 1, 1}

此索引 CONSTANT_NameAndType_info 类型的常量,表示方法名称及描述符

1E2 ~ 1E3:0x0015,源文件名索引(sourcefile_index),指向第 21 项 CONSTANT_Utf8_info 型常量:“Happy.java”;所以源文件名应为 “Happy”。

2A

强行运行会报错,抛出 UnsupportedClassVersionError:

u2

CONSTANT_Double_info

接下来是方法表(method_info)结构,用于描述方法。

数量

当虚拟机加载类型时,将会从常量池获得对应的符号引用,再在类创建时或运行时,解析、翻译到具体的内存地址之中。[2]

0xB1

1

0x0003:引导方法参数数量为 3。

03

u2

名称

u1

00000170:

// x = 1;对应两个指令

01

strictfp

标志值

// java/io/PrintStream.println:(Ljava/lang/String;)V

09

1

aload_0

名称

1

引导方法参数数量

类型

0C

类型

5: return?码 2.2.7-5 常量赋值?

特征签名(Signature)多了一项参数化类型的信息: “Ljava/util/List<Ljava/lang/String;>;”。

描述

?表 2.2.1-1 魔数与版本号?

flags:?表 2.2.6-1 方法表集合?

u2

09

06

// x = 3;

10

ldc 指令的参数,代表某字面量,指向第 6 项 CONSTANT_String_info 类型的常量:“Hello World”

影响:所有参数名称都会丢失,IDE 会使用 arg0、arg1 之类的占位符代替原有参数名,在调试期间无法根据参数名称从上下文中获取参数值。

08

这是 invokespecial 指令的参数,代表一个符号引用,指向第 1 项 CONSTANT_Methodref_info 类型的常量:java/lang/Object.&34;\<init>&34;:()V,说明应调用 Object 的无参构造

04

06

// java/lang/Object.&34;<init>&34;:()V

06

u2

attribute_name_index

1

0F

06

1

193 ~ 196:属性值占用的长度(attribute_length),0x00000030,转十进制为 48;从 197 ~ 1C6 都是 Code 属性的内容,这个属性表的长度为 48 + 6 = 54 个字节。

28

助记符

Java虚拟机的做法是将局部变量表中的变量槽进行重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占的变量槽可以被其他局部变量所复用,javac 编译器会根据变量的作用域来分配变量槽给各个变量使用,根据同时生存的最大局部变量数量和类型计算出 max_locals 的大小。

名称

0A

02

获取指定类的类变量,并将其值压入操作数栈顶

CONSTANT_InvokeDynamic_info

04

LineNumberTable

u2

Address

flags: ACC_PUBLIC

1

?码 2.2.6-1 异常处理源码及反编译后的指令?

final

0E

17

9 = Class

0B

09

08

char

无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1、2、4、8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码构成字符串值。

06

9.22

0: aload_0

名称

invokevirtual 指令的参数,指向第 7 项 CONSTANT_Methodref_info 类型的常量:java/io/PrintStream.println:(Ljava/lang/String;)V,代表调用 PrintStream 类的 println(String)方法。

invokevirtual

00000170:

177 ~ 178:字段访问标志(access_flags),识别字段的修饰符,0x0000。

12 = Utf8

155:第 38 项常量的标志位 0x01,属于 CONSTANT_Utf8_info 类型,代表 UTF-8 编码的字符串,用来表示字符串字面量、类型的全限定名、字段名、...[3]

01

u2

内容导视:

04

00

02

2 = Fieldref

0D

attribute_info

以下为例:

1

transient

interfaces_count

0x0001

?表 2.2.6-8 异常表结构?

1

?表 2.2.3-2 访问标志?

private

name_index:局部变量名索引

双精度浮点型字面量

0x0002

short

static

000001F0:

// 如果 [8, 13) 出现任意异常,转到 17:

constant_pool

名称

01

00

含义

1

8 Class java/lang/Exception

接下来是第 2 个字段;

?表 2.2.6-5 不同属性的含义?

仅当一个类为局部内部类或匿名类时才能拥有此属性,用于标识这个类所在的外围方法

()V

x = 3;

ACC_BRIDGE

L

11

x = 1;

2

1

07

B1

to target type

10 = Utf8

31

// i:I

01

接口类型

使用 javap 反编译工具,加 -verbose 参数输出 class 文件的部分内容:

16D ~ 16E:0x0030,访问标志(access_flags),表示类型的修饰符。

数量

3

不是运行必需的属性,可以编译时使用参数 -g:none 或 -g:vars 取消此项信息。

attribute_count

19: iconst_3 // 将常量 3 复制到操作数栈顶 {3} {this, ?, ?, ?, e}

00

对 class 文件的解析到此结束,下面是从 《深入理解 Java 虚拟机》摘抄的一部分属性表。

start_pc

F

long

06 ~ 07:主版本号(Major Version)0x0034,对应的十进制为 52,代表 JDK8;JDK1 从 45 开始,每隔一个大版本加 1;高版本的 JDK 可以向下兼容以前版本的 class 文件,反之则不行。

[2] class 文件里,所有的 “.” 都被斜杆 “/” 代替,例 “java.lang.Object” 变为 “java/lang/Object”。

这是 putfield 指令的参数,代表一个符号引用,指向第 2 项 CONSTANT_Fieldref_info 型常量:Happy.i:I,应为 Happy 类的 int 类型的变量名为 i 的变量赋值

1

0F

20 = Utf8

00

index:局部变量在栈帧的局部变量表中变量槽的位置。当变量数据类型大于 32 位时,它占用的变量槽为 index、index + 1 这两个。

5: istore_1 // 将 3 取出,存储到第 2 个变量槽 {} {this, 3, 1}

结束位置

u2

00

u4

字节码

07

ACC_TRANSIENT

// Field java/lang/System.out:Ljava/io/PrintStream;

}此字段表不会有 ConstantValue 属性,在 <clinit> 方法中赋值。

1F0 ~ 1F1:0x0002,行号表长度为 2。

0x06

public

0x1000

B5

?表 2.2.5-1 字段表集合?

1 个字节最多表示 256 条指令。目前,《Java虚拟机规范》已经定义了其中约 200 条编码值对应的指令含义,编码与指令之间的对应关系可查阅《深入理解 Java 虚拟机》的附录 C:“虚拟机字节码指令表”。

0x0036:转十进制为 54,指向第 54 项 CONSTANT_MethodHandle_info 型常量,代表一个方法句柄,REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(...)... 代表调用某静态方法。

00

0x0007

7

199 ~ 19A:0x0001,局部变量表所需的存储空间(max_locals)。max_locals 的单位是变量槽(Slot),变量槽是 JVM 为局部变量分配内存所使用的最小单位。

ACC_VOLATILE

9: aload_0

00000160:

ACC_ANNOTATION

length:作用范围覆盖的长度

volatile

00

0E

00

LineNumberTable:

操作数栈和局部变量表直接决定一个该方法的栈帧所耗费的内存,不必要的操作数栈深度和变量槽数量会造成内存的浪费。

0x0040

attributes_count

boolean

attribute_name_index

0000
评论列表
共(0)条