读懂 JVM 字节码
这篇文章是我为了方便以后的查找和快速记忆,在自己理解的基础上总结而成的文章,不是为新手准备的;
如果真的想轻松理解这篇文章里描述的东西,那么推荐你先把文章后面的参考链接仔细研读一遍。
0x00:字节码文件结构
总览:
ClassFile {
u4 magic; // 文件幻数
u2 minor_version; // 次版本号
u2 major_version; // 主版本号
u2 constant_pool_count; // 常量实体数量 + 1
cp_info contant_pool[constant_pool_count – 1]; // 常量实体表
u2 access_flags; // 访问权限标识
u2 this_class; // 类索引
u2 super_class; // 父类索引
u2 interfaces_count; // 接口实体数量
u2 interfaces[interfaces_count]; // 接口实体表
u2 fields_count; // 字段实体数量
field_info fields[fields_count]; // 字段实体表
u2 methods_count; // 方法实体数量
method_info methods[methods_count]; // 方法实体表
u2 attributes_count; // 属性实体数量
attribute_info attributes[attributes_count]; // 属性实体表
}
解释:
- 字节码文件结构总览从上到下来看,就是每项结构在文件中的出现顺序.第一行就是文件开头;
- 字节码文件结构总览从左到右来看,最左侧表示每项所占字节大小(有的大小不固定),右侧是每项结构说明;
- u + 数字, 代表占用的空间大小也叫类型. 第一个
u4
就代表此项结构在字节码文件中占用 4 个字节(bytes)
实体结构从上至下,依次为:
cp_info 结构
由不固定数量的常量实体组成;
常量实体表见下文 "0x01 —— 14种数据类型常量结构表"
class access_flags 表
访问控制标识符 | 值 | 解释 |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
ACC_FINAL | 0x0010 | Declared final; no subclasses allowed. |
ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the invokespecial instruction. |
ACC_INTERFACE | 0x0200 | Is an interface, not a class. |
ACC_ABSTRACT | 0x0400 | Declared abstract; must not be instantiated. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |
ACC_ENUM | 0x4000 | Declared as an enum type. |
field_info 结构
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
field_info access_flags 表
Flag Name | Value | Interpretation |
---|---|---|
ACC_PUBLIC |
0x0001 | Declared public ; may be accessed from outside its package. |
ACC_PRIVATE |
0x0002 | Declared private ; usable only within the defining class. |
ACC_PROTECTED |
0x0004 | Declared protected ; may be accessed within subclasses. |
ACC_STATIC |
0x0008 | Declared static . |
ACC_FINAL |
0x0010 | Declared final ; never directly assigned to after object construction (JLS §17.5). |
ACC_VOLATILE |
0x0040 | Declared volatile ; cannot be cached. |
ACC_TRANSIENT |
0x0080 | Declared transient ; not written or read by a persistent object manager. |
ACC_SYNTHETIC |
0x1000 | Declared synthetic; not present in the source code. |
ACC_ENUM |
0x4000 | Declared as an element of an enum . |
method_info 结构
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
method_info access_flags 表
Flag Name | Value | Interpretation |
---|---|---|
ACC_PUBLIC |
0x0001 | Declared public ; may be accessed from outside its package. |
ACC_PRIVATE |
0x0002 | Declared private ; accessible only within the defining class. |
ACC_PROTECTED |
0x0004 | Declared protected ; may be accessed within subclasses. |
ACC_STATIC |
0x0008 | Declared static . |
ACC_FINAL |
0x0010 | Declared final ; must not be overridden (§5.4.5). |
ACC_SYNCHRONIZED |
0x0020 | Declared synchronized ; invocation is wrapped by a monitor use. |
ACC_BRIDGE |
0x0040 | A bridge method, generated by the compiler. |
ACC_VARARGS |
0x0080 | Declared with variable number of arguments. |
ACC_NATIVE |
0x0100 | Declared native ; implemented in a language other than Java. |
ACC_ABSTRACT |
0x0400 | Declared abstract ; no implementation is provided. |
ACC_STRICT |
0x0800 | Declared strictfp ; floating-point mode is FP-strict. |
ACC_SYNTHETIC |
0x1000 | Declared synthetic; not present in the source code. |
attribute_info 结构
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
attribute 结构
Attribute | Details |
---|---|
ConstantValue |
§4.7.2 |
Code |
§4.7.3 |
StackMapTable |
§4.7.4 |
Exceptions |
§4.7.5 |
InnerClasses |
§4.7.6 |
EnclosingMethod |
§4.7.7 |
Synthetic |
§4.7.8 |
Signature |
§4.7.9 |
SourceFile |
§4.7.10 |
SourceDebugExtension |
§4.7.11 |
LineNumberTable |
§4.7.12 |
LocalVariableTable |
§4.7.13 |
LocalVariableTypeTable |
§4.7.14 |
Deprecated |
§4.7.15 |
RuntimeVisibleAnnotations |
§4.7.16 |
RuntimeInvisibleAnnotations |
§4.7.17 |
RuntimeVisibleParameterAnnotations |
§4.7.18 |
RuntimeInvisibleParameterAnnotations |
§4.7.19 |
AnnotationDefault |
§4.7.20 |
BootstrapMethods |
§4.7.21 |
0x01:相关项解释
文件幻数:
挺有意思,值为十六进制的 cafe babe (咖啡馆宝贝)
主版本与适配的JDK版本:
major_version (十进制) | major_version (十六进制) | JDK version |
---|---|---|
46 | 0x2e | 1.2 |
47 | 0x2f | 1.3 |
48 | 0x30 | 1.4 |
49 | 0x31 | 1.5 |
50 | 0x32 | 1.6 |
51 | 0x33 | 1.7 |
52 | 0x34 | 1.8 |
14种数据类型常量结构表:
- 总共十四种常量实体类型,每一种都有一个
tag
项,tag
项的值(全部占一字节)标识它是哪种类型的常量 - 常量池中存放着字符串常量、类和接口的全限定名引用、字段的名称和修饰符引用和方法的名称和修饰符引用
- 一旦确定常量实体的种类,那么它所占用的总字节数就可以根据这种常量的固定结构计算出来
类索引
this_class
保存当前类的全限定名的索引super_class
保存当前类的父类的全限定名的索引
0x02:手工分析字节码
一:准备工作
- 写一段简单代码保存为
hello.java
文件.
public class hello {
protected String w = "world";
public static String test(String str){
return str;
}
public static void main(String[] args) {
System.out.print("LandGrey");
}
}
-
用命令
javac -target 1.6 -source 1.6 hello.java
将hello.java
编译为 class 字节码文件. -
用
winhex
观察hello.class
字节码文件.
二:开始解读
对于下图中的标记:
意义分别如下:
标记 | 值 | 意义 |
---|---|---|
1 | CA FE BA BE | 字节码文件幻数,固定值 |
2 | 00 00 | minor_version 值为零 |
3 | 00 32 | major_version 值为 0x32,对应 JVM 版本为 1.6 |
4 | 00 25 | constant_pool_count 值 为 0x25,代表接下来的常量池中共有 36 个常量实体 |
三:常量池解读
常量池中36 个具体的常量实体如下图标记所示:
- 第一个蓝色框
00 25
后面的所有被不同颜色框选中的为具体的 36 个常量实体 - 被红色框选中的,都是
CONSTANT_Utf8_info
类型实体,它们每个后面都跟着被length
长度的字符串(即后面紧跟着的未被任何颜色框中的部分) - 注意由于换行原因,最右侧的一个框可能和它下面一行的开始的框是一个整体,属于一个常量实体
四:36个常量实体次序表
根据常量池解读,依照次序列出常量实体表,方便后续的整体分析. 常量实体过长的,用 ~
表示中间部分省略.
次序 | 常量实体 | 类别 | 说明 |
---|---|---|---|
1 | 0A 00 08 00 15 | CONSTANT_Methodref_info | 索引分别指向第8个常量,第21个常量 |
2 | 08 00 16 | CONSTANT_String_info | 索引指向第22个字符串常量world |
3 | 09 00 07 00 17 | CONSTANT_Fieldref_info | 索引分别指向第7个常量,第23个常量 |
4 | 09 00 18 00 19 | CONSTANT_Fieldref_info | 索引分别指向第24个常量,第25个常量 |
5 | 08 00 1A | CONSTANT_String_info | 索引指向第26个字符串常量LandGrey |
6 | 0A 00 1B 00 1C | CONSTANT_Methodref_info | 索引分别指向第27个常量,第28个常量 |
7 | 07 00 1D | CONSTANT_Class_info | 索引指向第29个常量hello |
8 | 07 00 1E | CONSTANT_Class_info | 索引指向第30个常量java/lang/Object |
9 | 01 00 01 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为1个字节 |
77 | 字符串w |
||
10 | 01 00 12 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为18个字节 |
4C6A ~ 673B | 字符串Ljava/lang/String; |
||
11 | 01 00 06 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为6个字节 |
3C696E69743E | 字符串<init> |
||
12 | 01 00 03 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为3个字节 |
28 29 56 | 字符串()V |
||
13 | 01 00 04 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为4个字节 |
436F6465 | 字符串Code |
||
14 | 01 00 0F | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为15个字节 |
4C69 ~ 6C65 | 字符串LineNumberTable |
||
15 | 01 00 04 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为4个字节 |
74657374 | 字符串test |
||
16 | 01 00 26 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为38个字节 |
284C ~ 673B | 字符串(Ljava/lang/String;)Ljava/lang/String; |
||
17 | 01 00 04 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为4个字节 |
6D61696E | 字符串main |
||
18 | 01 00 16 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为22个字节 |
285B ~ 2956 | 字符串([Ljava/lang/String;)V |
||
19 | 01 00 0A | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为10个字节 |
536F ~ 6C65 | 字符串SourceFile |
||
20 | 01 00 0A | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为10个字节 |
6865 ~ 7661 | 字符串hello.java |
||
21 | 0C 00 0B 00 0C | CONSTANT_NameAndType_info | 索引分别指向第11个常量,第12个常量 |
22 | 01 00 05 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为5个字节 |
776F726C64 | 字符串world |
||
23 | 0C 00 09 00 0A | CONSTANT_NameAndType_info | 索引分别指向第9个常量,第10个常量 |
24 | 07 00 1F | CONSTANT_Class_info | 索引指向第31个常量 |
25 | 0C 00 20 00 21 | CONSTANT_NameAndType_info | 索引分别指向第32个常量,第33个常量 |
26 | 01 00 08 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为8个字节 |
4C61 ~ 6579 | 字符串LandGrey |
||
27 | 07 00 22 | CONSTANT_Class_info | 索引指向第34个常量 |
28 | 0C 00 23 00 24 | CONSTANT_NameAndType_info | 索引分别指向第35个常量,第36个常量 |
29 | 01 00 05 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为5个字节 |
68656C6C6F | 字符串hello |
||
30 | 01 00 10 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为16个字节 |
6A61 ~ 6374 | 字符串java/lang/Object |
||
31 | 01 00 10 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为16个字节 |
6A61 ~ 656D | 字符串java/lang/System |
||
32 | 01 00 03 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为3个字节 |
6F7574 | 字符串out |
||
33 | 01 00 15 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为21个字节 |
4C6A ~ 6D3B | 字符串Ljava/io/PrintStream; |
||
34 | 01 00 13 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为19个字节 |
6A61 ~ 616D | 字符串java/io/PrintStream |
||
35 | 01 00 05 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为5个字节 |
7072696E74 | 字符串print |
||
36 | 01 00 15 | CONSTANT_Utf8_info | 表示后面跟着的字符串长度为21个字节 |
284C ~ 2956 | 字符串(Ljava/lang/String;)V |
五:解读其余字节码
- 严格按照本文
0x00
部分和参考链接1
的字节码文件结构进行字节码解读 - 其余很多结构都会引用
四:36个常量实体次序表
中的常量实体 - 剩余部分内容太多,所以只列出关键的基本引用信息
- start_pc代表字节码偏移量(方法第几条指令),line_number代表源码行号,调试信息,非必须项
值 实体 含义
0021 access_flags ACC_PUBLIC + ACC_SUPER
0007 this_class 指向字符串 hello
0008 super_class 指向字符串 java/lang/Object
0000 interfaces_count 无接口,所以后面也无接口实体列表
0001 fields_count 字段数量为1个
0004 field_info[1] 第一个字段的access_flags标志为 ACC_PROTECTED
0009 field_info[1] 第一个字段的name_index指向字符串 w
000A field_info[1] 第一个字段的descriptor_index指向字符串 Ljava/lang/String;
0000 field_info[1] 第一个字段的attributes_count数量为零
0003 methods_count 方法数量为3个
0001 method_info[1] 第一个方法的access_flags标志为 ACC_PUBLIC
000B method_info[1] 第一个方法的name_index指向字符串 <init> (方法)
000C method_info[1] 第一个方法的descriptor_index指向字符串 ()V
0001 method_info[1] 第一个方法的attributes_count数量为1个
000D m[1]attributes[1] 第一个方法的Code属性的attribute_name_index指向字符串 Code
00000027 m[1]attributes[1] 第一个方法的Code属性的attribute_length值为39(后面取39个字节)
0002 m[1]attributes[1] 第一个方法的Code属性的max_stack值为2
0001 m[1]attributes[1] 第一个方法的Code属性的max_locals值为1
0000000B m[1]attributes[1] 第一个方法的Code属性的code_length值为11
2AB700012A1202B50003B1 m[1]attributes[1] 第一个方法的Code属性的code值
0000 m[1]attributes[1] 第一个方法的Code属性的exception_table_length值为0
0001 m[1]attributes[1] 第一个方法的Code属性的attributes_count值为1
000E m[1]a[1]a[1] 第一个方法的Code属性的第一个属性attribute_name_index指向字符串LineNumberTabl
0000000A m[1]a[1]a[1] 第一个方法的Code属性的LineNumberTabl属性的attribute_length值为10
0002 m[1]a[1]a[1] 第一个方法的Code属性的LineNumberTabl属性的line_number_table_length值为2
0000 0001 m[1]a[1]a[1] 第一个方法的Code属性的LineNumberTabl属性的第1个start_pc为0 line_number为1
0004 0002 m[1]a[1]a[1] 第一个方法的Code属性的LineNumberTabl属性的第2个start_pc为4 line_number为2
0009 method_info[2] 第二个方法的access_flags标志为 ACC_PUBLIC + ACC_STATIC
000F method_info[2] 第二个方法的name_index指向字符串 test (方法)
0010 method_info[2] 第二个方法的descriptor_index指向字符串 (Ljava/lang/String;)Ljava/lang/String;
0001 method_info[2] 第二个方法的attributes_count数量为1个
000D m[2]attributes[1] 第二个方法的Code属性的attribute_name_index指向字符串 Code
0000001A m[2]attributes[1] 第二个方法的Code属性的attribute_length值为26
0001 m[2]attributes[1] 第二个方法的Code属性的max_stack值为1
0001 m[2]attributes[1] 第二个方法的Code属性的max_locals值为1
00000002 m[2]attributes[1] 第二个方法的Code属性的code_length值为2
2AB0 m[2]attributes[1] 第二个方法的Code属性的code值
0000 m[2]attributes[1] 第二个方法的Code属性的exception_table_length值为0
0001 m[2]attributes[1] 第二个方法的Code属性的attributes_count值为1
000E m[2]a[1]a[1] 第二个方法的Code属性的第一个属性attribute_name_index指向字符串LineNumberTabl
00000006 m[2]a[1]a[1] 第二个方法的Code属性的LineNumberTabl属性的attribute_length值为6
0001 m[2]a[1]a[1] 第二个方法的Code属性的LineNumberTabl属性的line_number_table_length值为1
0000 0005 m[2]a[1]a[1] 第二个方法的Code属性的LineNumberTabl属性的第1个start_pc为0 line_number为5
0009 method_info[3] 第三个方法的access_flags标志为 ACC_PUBLIC + ACC_STATIC
0011 method_info[3] 第三个方法的name_index指向字符串 main
0012 method_info[3] 第三个方法的descriptor_index指向字符串 ([Ljava/lang/String;)V
0001 method_info[3] 第三个方法的attributes_count数量为1个
000D m[3]attributes[1] 第三个方法的Code属性的attribute_name_index指向字符串 Code
00000025 m[3]attributes[1] 第三个方法的Code属性的attribute_length值为37
0002 m[3]attributes[1] 第三个方法的Code属性的max_stack值为2
0001 m[3]attributes[1] 第三个方法的Code属性的max_locals值为1
00000009 m[3]attributes[1] 第三个方法的Code属性的code_length值为9
B200041205B60006B1 m[3]attributes[1] 第二个方法的Code属性的code值
0000 m[3]attributes[1] 第三个方法的Code属性的exception_table_length值为0
0001 m[3]attributes[1] 第三个方法的Code属性的attributes_count值为1
000E m[3]a[1]a[1] 第三个方法的Code属性的第一个属性attribute_name_index指向字符串LineNumberTabl
0000000A m[3]a[1]a[1] 第三个方法的Code属性的LineNumberTabl属性的attribute_length值为10
0002 m[3]a[1]a[1] 第三个方法的Code属性的LineNumberTabl属性的line_number_table_length值为2
0000 0009 m[3]a[1]a[1] 第三个方法的Code属性的LineNumberTabl属性的第1个start_pc为0 line_number为9
0008 000A m[3]a[1]a[1] 第三个方法的Code属性的LineNumberTabl属性的第2个start_pc为8 line_number为10
0001 attributes_count attributes_count值为1
0013 attributes[1] 第一个属性的attribute_name_index指向 SourceFile 字符串
00000002 attributes[1] 第一个属性的attribute_length为2
0014 attributes[1] 第一个属性的sourcefile_index指向常量池中第20个常量 hello.java
0x03:javap分析字节码
人工用二进制查看器分析class 字节码既麻烦又复杂,所以使用Oracle官方提供的 javap
工具:
>javap -v -p -l hello.class
Classfile /path/to/hello.class
Last modified 2019-9-18; size 562 bytes
MD5 checksum 2f562da266fabaa7affd5e0cca172152
Compiled from "hello.java"
public class hello
minor version: 0
major version: 50
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#21 // java/lang/Object."<init>":()V
#2 = String #22 // world
#3 = Fieldref #7.#23 // hello.w:Ljava/lang/String;
#4 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
#5 = String #26 // LandGrey
#6 = Methodref #27.#28 // java/io/PrintStream.print:(Ljava/lang/String;)V
#7 = Class #29 // hello
#8 = Class #30 // java/lang/Object
#9 = Utf8 w
#10 = Utf8 Ljava/lang/String;
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 test
#16 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 SourceFile
#20 = Utf8 hello.java
#21 = NameAndType #11:#12 // "<init>":()V
#22 = Utf8 world
#23 = NameAndType #9:#10 // w:Ljava/lang/String;
#24 = Class #31 // java/lang/System
#25 = NameAndType #32:#33 // out:Ljava/io/PrintStream;
#26 = Utf8 LandGrey
#27 = Class #34 // java/io/PrintStream
#28 = NameAndType #35:#36 // print:(Ljava/lang/String;)V
#29 = Utf8 hello
#30 = Utf8 java/lang/Object
#31 = Utf8 java/lang/System
#32 = Utf8 out
#33 = Utf8 Ljava/io/PrintStream;
#34 = Utf8 java/io/PrintStream
#35 = Utf8 print
#36 = Utf8 (Ljava/lang/String;)V
{
protected java.lang.String w;
descriptor: Ljava/lang/String;
flags: ACC_PROTECTED
public hello();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String world
7: putfield #3 // Field w:Ljava/lang/String;
10: return
LineNumberTable:
line 1: 0
line 2: 4
public static java.lang.String test(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 5: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String LandGrey
5: invokevirtual #6 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 9: 0
line 10: 8
}
SourceFile: "hello.java"
和手工分析的字节码对比,可以发现很多细节都能对应上,有一种恍然大悟的感觉.
0x04:字节码指令
现在,字节码结构能够大概理解了,但是其中还有三个方法的Code属性的code值,也就是字节码指令没有介绍。这部分内容很庞大,单独拿出来都能写几篇文章了,只做简单介绍.
-
JVM 字节码指令由
一个字节长度的操作码
和零至多个长度的操作数
组成. -
JVM 字节码指令是面向
后进先出
的操作数栈
进行入栈
和出栈
操作的. -
JVM 字节码指令的
加载
和存储
是将数据从局部变量表
和操作数栈
进行交替传输的过程 -
每个变量都占局部变量区中的一个变量槽(slot),long及double会占用两个连续的变量槽
一张数据类型为行,操作指令为列,引用自网络资源的操作指令表如下:
解答下上面案列中3个方法的字节码指令的含义:
第一处字节码指令
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String world
7: putfield #3 // Field w:Ljava/lang/String;
10: return
LineNumberTable:
line 1: 0
line 2: 4
指令含义
aload_0 从局部变量数组中加载一个对象引用(this)到操作数栈的栈顶
invokespecial 调用对象实例java.lang.Object的构造方法
aload_0 从局部变量数组中加载一个对象引用到操作数栈的栈顶
ldc 将常量 "world" 从常量池推送到操作数栈顶
putfield 为实例字段 "w" 赋值 "world"
return 从当前方法返回void
第二处字节码指令
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 5: 0
指令含义
aload_0 从局部变量数组中加载一个对象引用到操作数栈的栈顶
areturn 返回栈顶元素
第三处字节码指令
Code:
stack=2, locals=1, args_size=1
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String LandGrey
5: invokevirtual #6 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 9: 0
line 10: 8
指令含义
getstatic 将static引用变量推送到栈顶
ldc 将常量 "LandGrey" 从常量池推送到操作数栈顶
invokevirtual 调用对象的实例方法
return 从当前方法返回void
0x05:参考链接
- Chapter 4. The
class
File Format - 深入理解JVM之Java字节码(.class)文件详解
- The Java® Virtual Machine Specification
- 一文让你明白Java字节码
- JAVA基础回顾-字节码指令
- Java字节码浅析(—)
评论