登陆注册
16942

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

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

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”

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

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

0000
评论列表
共(0)条
热点
关注
推荐