Article directory
1. Bytecode viewing method
The .class file itself is a binary bytecode, which is too obscure to read directly. When we look at it here, we use some disassembly tools to view it.
1.1、javap
javap
Can decompile bytecode files. You can understand the basic usage of javap through javap -help
the command .
D:\workspace\DemoTest\out\production\DemoTest\com\leo\test>javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
1.2、jclasslib
jclasslib Bytecode Viewer
It is a plug-in in the IDEA development tool, which can easily view the compiled bytecode file of each java class. There are many specific installation methods, so I won't repeat them here.
2. Bytecode analysis
2.1. Code before compilation
package com.leo.test;
public class ByteCodeTest {
public static int num = 1;
public static int add(byte a, short b, int c, String d, float e, double f) {
int d_int = Integer.parseInt(d);
return (int) (a + b + c + d_int + e + f);
}
public static void main(String[] args) {
byte a = (byte) 0xFF; // -1
short b = 2;
int c = 3;
String d = "4";
float e = 5.0f;
double f = 6.0d;
boolean bool = false;
int g = add(a, b, c, d, e, f);
System.out.println(num); // 1
System.out.println(g); // 19
}
}
2.2. After compilation
Use the javap command to view the bytecode:
javap -c -verbose ByteCodeTest.class
The content of the decompiled file using javap is as follows:
Classfile /D:/workspace/DemoTest/out/production/DemoTest/com/leo/test/ByteCodeTest.class
Last modified 2022-6-28; size 1136 bytes
MD5 checksum 6550d8d70ee59d88e1ec1614d1b6f1e1
Compiled from "ByteCodeTest.java"
public class com.leo.test.ByteCodeTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#46 // java/lang/Object."<init>":()V
#2 = Methodref #47.#48 // java/lang/Integer.parseInt:(Ljava/lang/String;)I
#3 = String #49 // 4
#4 = Float 5.0f
#5 = Double 6.0d
#7 = Methodref #11.#50 // com/leo/test/ByteCodeTest.add:(BSILjava/lang/String;FD)I
#8 = Fieldref #51.#52 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Fieldref #11.#53 // com/leo/test/ByteCodeTest.num:I
#10 = Methodref #54.#55 // java/io/PrintStream.println:(I)V
#11 = Class #56 // com/leo/test/ByteCodeTest
#12 = Class #57 // java/lang/Object
#13 = Utf8 num
#14 = Utf8 I
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 Lcom/leo/test/ByteCodeTest;
#22 = Utf8 add
#23 = Utf8 (BSILjava/lang/String;FD)I
#24 = Utf8 a
#25 = Utf8 B
#26 = Utf8 b
#27 = Utf8 S
#28 = Utf8 c
#29 = Utf8 d
#30 = Utf8 Ljava/lang/String;
#31 = Utf8 e
#32 = Utf8 F
#33 = Utf8 f
#34 = Utf8 D
#35 = Utf8 d_int
#36 = Utf8 main
#37 = Utf8 ([Ljava/lang/String;)V
#38 = Utf8 args
#39 = Utf8 [Ljava/lang/String;
#40 = Utf8 bool
#41 = Utf8 Z
#42 = Utf8 g
#43 = Utf8 <clinit>
#44 = Utf8 SourceFile
#45 = Utf8 ByteCodeTest.java
#46 = NameAndType #15:#16 // "<init>":()V
#47 = Class #58 // java/lang/Integer
#48 = NameAndType #59:#60 // parseInt:(Ljava/lang/String;)I
#49 = Utf8 4
#50 = NameAndType #22:#23 // add:(BSILjava/lang/String;FD)I
#51 = Class #61 // java/lang/System
#52 = NameAndType #62:#63 // out:Ljava/io/PrintStream;
#53 = NameAndType #13:#14 // num:I
#54 = Class #64 // java/io/PrintStream
#55 = NameAndType #65:#66 // println:(I)V
#56 = Utf8 com/leo/test/ByteCodeTest
#57 = Utf8 java/lang/Object
#58 = Utf8 java/lang/Integer
#59 = Utf8 parseInt
#60 = Utf8 (Ljava/lang/String;)I
#61 = Utf8 java/lang/System
#62 = Utf8 out
#63 = Utf8 Ljava/io/PrintStream;
#64 = Utf8 java/io/PrintStream
#65 = Utf8 println
#66 = Utf8 (I)V
{
public static int num;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
public com.leo.test.ByteCodeTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/leo/test/ByteCodeTest;
public static int add(byte, short, int, java.lang.String, float, double);
descriptor: (BSILjava/lang/String;FD)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=8, args_size=6
0: aload_3
1: invokestatic #2 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
4: istore 7
6: iload_0
7: iload_1
8: iadd
9: iload_2
10: iadd
11: iload 7
13: iadd
14: i2f
15: fload 4
17: fadd
18: f2d
19: dload 5
21: dadd
22: d2i
23: ireturn
LineNumberTable:
line 7: 0
line 8: 6
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 a B
0 24 1 b S
0 24 2 c I
0 24 3 d Ljava/lang/String;
0 24 4 e F
0 24 5 f D
6 18 7 d_int I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=10, args_size=1
0: iconst_m1
1: istore_1
2: iconst_2
3: istore_2
4: iconst_3
5: istore_3
6: ldc #3 // String 4
8: astore 4
10: ldc #4 // float 5.0f
12: fstore 5
14: ldc2_w #5 // double 6.0d
17: dstore 6
19: iconst_0
20: istore 8
22: iload_1
23: iload_2
24: iload_3
25: aload 4
27: fload 5
29: dload 6
31: invokestatic #7 // Method add:(BSILjava/lang/String;FD)I
34: istore 9
36: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
39: getstatic #9 // Field num:I
42: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
45: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
48: iload 9
50: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
53: return
LineNumberTable:
line 12: 0
line 13: 2
line 14: 4
line 15: 6
line 16: 10
line 17: 14
line 18: 19
line 19: 22
line 20: 36
line 21: 45
line 22: 53
LocalVariableTable:
Start Length Slot Name Signature
0 54 0 args [Ljava/lang/String;
2 52 1 a B
4 50 2 b S
6 48 3 c I
10 44 4 d Ljava/lang/String;
14 40 5 e F
19 35 6 f D
22 32 8 bool Z
36 18 9 g I
static {
};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_1
1: putstatic #9 // Field num:I
4: return
LineNumberTable:
line 4: 0
}
SourceFile: "ByteCodeTest.java"
2.3. Bytecode structure
The bytecode structure given by Oracle official is quoted as follows:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_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];
}
explain
type | name | explain | length | quantity |
---|---|---|---|---|
u4 | magic | magic number | 4 bytes | 1 |
u2 | minor_version | minor version number | 2 bytes | 1 |
u2 | major_version | major version number | 2 bytes | 1 |
u2 | constant_pool_count | constant pool size | 2 bytes | 1 |
cp_info | constant_pool[] | constant pool | n bytes | costant_pool_count - 1 |
u2 | access_flags | class access control permissions | 2 bytes | 1 |
u2 | this_class | class index | 2 bytes | 1 |
u2 | super_class | parent class index | 2 bytes | 1 |
u2 | interfaces_count | Number of Interface Indexes | 2 bytes | 1 |
u2 | interfaces[] | Implemented interface content | 2 bytes | interfaces_count |
u2 | fields_count | Number of member properties | 2 bytes | 1 |
field_info | fields[] | member attribute value | n bytes | fields_count |
u2 | methods_count | number of methods | 2 bytes | 1 |
method_info | methods[] | method table | n bytes | methods_count |
u2 | attributes_count | number of class attributes | 2 bytes | 1 |
attribute_info | attributes[] | class attribute value | n bytes | attributes_count |
-
Unsigned numbers are the most basic data types . It uses u1, u2, u4, and u8 to represent unsigned numbers of 1 byte, 2 bytes, 4 bytes, and 8 bytes respectively. Unsigned numbers can be used to describe numbers, index references, quantity values, or string values formed according to the UTF-8 encoding. For example, u4 in the first line of the following table means that the first 4 bytes of the Class file represent the magic number of the file, and u2 in the second line means that the 5th to 6th bytes of the Class file represent the minor version number of the JDK.
-
A table is a composite data type composed of multiple unsigned numbers or other tables as data items . All tables habitually end with _info. Tables are used to describe data with a composite structure with hierarchical relationships. For example, row 5 of the following table indicates a table (constant pool) of type cp_info, which stores all constants of this class.
Borrow a schematic diagram of the bytecode structure organization of a Class file :
2.4. Simplified understanding of bytecode structure
2.4.1, Class file structure
A typical class file is divided into ten parts: MagicNumber, Version, Constant_pool, Access_flag, This_class, Super_class, Interfaces, Fields, Methods and Attributes.
But for the convenience of understanding, the simplified model is borrowed: Class摘要
, 常量池
,方法栈帧
2.4.2, Class Summary
The summary is mainly to record some basic information of the class, including the size of the class, modification time, checksum, JDK version information, access range of the class, etc.
D:\workspace\DemoTest\out\production\DemoTest\com\leo\test>javap -v ByteCodeTest.class // 反编译字节码文件 ByteCodeTest.class
Classfile /D:/workspace/DemoTest/out/production/DemoTest/com/leo/test/ByteCodeTest.class // 文件来源,可以看到文件的路径
Last modified 2022-6-28; size 1136 bytes // 类最后修改时间、类大小
MD5 checksum 6550d8d70ee59d88e1ec1614d1b6f1e1 // 校验和
Compiled from "ByteCodeTest.java" // 编译的文件
public class com.leo.test.ByteCodeTest // 全类名
minor version: 0 // jdk 子版本号
major version: 52 // jdk 主版本号,52 代表的就是 jdk1.8
flags: ACC_PUBLIC, ACC_SUPER // 说明这个类是 public 类,初始化方法使用父类的
2.4.3. Constant pool
- Literal quantity : including text strings, final modified member variables, and data values, etc. For constants of the basic numeric int type, the constant pool value saves references and literal names, but does not save data values
#3 = String #49 // 4,引用的是 #49 行的值
#4 = Float 5.0f // float 类型 5.0
#5 = Double 6.0d // double 类型 6.0
#28 = Utf8 c // int 类型只保存了名称,没有存实际值
......中间省略了
#49 = Utf8 4
- Symbolic references : names and descriptions of classes and interfaces
#7 = Methodref #11.#50 // com/leo/test/ByteCodeTest.add:(BSILjava/lang/String;FD)I
#8 = Fieldref #51.#52 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Fieldref #11.#53 // com/leo/test/ByteCodeTest.num:I
#10 = Methodref #54.#55 // java/io/PrintStream.println:(I)V
#11 = Class #56 // com/leo/test/ByteCodeTest
#12 = Class #57 // java/lang/Object
#57 = Utf8 java/lang/Object
......中间省略了
#50 = NameAndType #22:#23 // add:(BSILjava/lang/String;FD)I
#51 = Class #61 // java/lang/System
#52 = NameAndType #62:#63 // out:Ljava/io/PrintStream;
#53 = NameAndType #13:#14 // num:I
#54 = Class #64 // java/io/PrintStream
#55 = NameAndType #65:#66 // println:(I)V
2.4.4, method stack frame
The two methods add and main methods given in the code sample:
public static int add(byte, short, int, java.lang.String, float, double);
descriptor: (BSILjava/lang/String;FD)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=8, args_size=6
0: aload_3
1: invokestatic #2 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
4: istore 7
6: iload_0
7: iload_1
8: iadd
9: iload_2
10: iadd
11: iload 7
13: iadd
14: i2f
15: fload 4
17: fadd
18: f2d
19: dload 5
21: dadd
22: d2i
23: ireturn
LineNumberTable:
line 7: 0
line 8: 6
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 a B
0 24 1 b S
0 24 2 c I
0 24 3 d Ljava/lang/String;
0 24 4 e F
0 24 5 f D
6 18 7 d_int I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=10, args_size=1
0: iconst_m1
1: istore_1
2: iconst_2
3: istore_2
4: iconst_3
5: istore_3
6: ldc #3 // String 4
8: astore 4
10: ldc #4 // float 5.0f
12: fstore 5
14: ldc2_w #5 // double 6.0d
17: dstore 6
19: iconst_0
20: istore 8
22: iload_1
23: iload_2
24: iload_3
25: aload 4
27: fload 5
29: dload 6
31: invokestatic #7 // Method add:(BSILjava/lang/String;FD)I
34: istore 9
36: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
39: getstatic #9 // Field num:I
42: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
45: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
48: iload 9
50: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
53: return
LineNumberTable:
line 12: 0
line 13: 2
line 14: 4
line 15: 6
line 16: 10
line 17: 14
line 18: 19
line 19: 22
line 20: 36
line 21: 45
line 22: 53
LocalVariableTable:
Start Length Slot Name Signature
0 54 0 args [Ljava/lang/String;
2 52 1 a B
4 50 2 b S
6 48 3 c I
10 44 4 d Ljava/lang/String;
14 40 5 e F
19 35 6 f D
22 32 8 bool Z
36 18 9 g I
2.4.4.1, stack frame summary
// add 方法
public static int add(byte, short, int, java.lang.String, float, double);
descriptor: (BSILjava/lang/String;FD)I // 返回值 I 指的是 int 类型
flags: ACC_PUBLIC, ACC_STATIC // 公共方法、静态方法
Code:
stack=4, locals=8, args_size=6 // 栈帧深度是4,最大局部变量8个,入参个数6个
// main 方法
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V // 返回值 V 指的是 void 类型
flags: ACC_PUBLIC, ACC_STATIC // 公共方法、静态方法
Code:
stack=7, locals=10, args_size=1 // 栈帧深度是7,最大局部变量10个,入参个数1个
2.4.4.2, local variable table
The local variable table is an array used to store the local variables of the current method. The types that can be stored in the table include boolean, byte, char, short, int, float, and reference types. Because local variables are private to threads, there will be no For thread safety issues, the size of the local variable table in each stack frame is determined after the .class bytecode file is compiled.
// add 方法
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 a B
0 24 1 b S
0 24 2 c I
0 24 3 d Ljava/lang/String;
0 24 4 e F
0 24 5 f D
6 18 7 d_int I
// main 方法
LocalVariableTable:
Start Length Slot Name Signature
0 54 0 args [Ljava/lang/String; // 入参是 args ,类型是 String 数组
2 52 1 a B
4 50 2 b S
6 48 3 c I
10 44 4 d Ljava/lang/String; // 局部变量,类型是 String
14 40 5 e F
19 35 6 f D
22 32 8 bool Z
36 18 9 g I
In the JVM specification, each variable/field has descriptive information. The main function of the descriptive information is to describe the data type of the field, the parameter list (including quantity, type and order) and the return value of the method. According to the descriptor rules, the basic data Both the type and the void type representing no return value are represented by an uppercase character, and the object type is represented by the character L plus the fully qualified name of the object, in order to compress the size of the bytecode file. For basic data types, the JVM uses only one uppercase letter to represent them, as follows:
2.4.4.3, operand stack
# main方法的操作数栈:
0: iconst_m1 // 将 int 类型常量 -1 压入栈, 这个就是 a 的值, byte 操作的时候会补齐高位按照 int 值操作
1: istore_1 // 将 int 类型值存入局部变量 1
2: iconst_2 // 将 int 类型常量 2 压入栈
3: istore_2 // 将 int 类型值存入局部变量 2
4: iconst_3 // 将 int 类型常量 3 压入栈
5: istore_3 // 将 int 类型值存入局部变量 3
6: ldc #3 // String 4, 将常量池中的项压入栈
8: astore 4 // 将引用类型值存入局部变量 4
10: ldc #4 // float 5.0f, 把常量池中的项压入栈
12: fstore 5 // 将 float 类型值存入局部变量 5
14: ldc2_w #5 // double 6.0d, 把常量池中的 double 类型的项压入栈(使用宽索引,long类型也是)
17: dstore 6 // 将 double 类型值存入局部变量 6
19: iconst_0 // 将 int 类型常量 0 压入栈,boolean 的值只有两个: true=1, false=0
20: istore 8 // 将 int 类型值存入局部变量 8
22: iload_1 // 从局部变量 1 中装载 int 类型值
23: iload_2 // 从局部变量 2 中装载 int 类型值
24: iload_3 // 从局部变量 3 中装载 int 类型值
25: aload 4 // 从局部变量 4 中装载 引用 类型值
27: fload 5 // 从局部变量 5 中装载 float 类型值
29: dload 6 // 从局部变量 6 中装载 double 类型值
31: invokestatic #7 // Method add:(BSILjava/lang/String;FD)I, 调用类静态方法 add
34: istore 9 // 将 int 类型值存入局部变量
36: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;, 从类中获取静态字段
39: getstatic #9 // Field num:I, 从类中获取静态字段 num 类型为 int
42: invokevirtual #10 // Method java/io/PrintStream.println:(I)V, 调度对象的实现方法
45: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;, 从类中获取静态字段
48: iload 9 // 从局部变量 9 中装载 int 类型值
50: invokevirtual #10 // Method java/io/PrintStream.println:(I)V,, 调度对象的实现方法
53: return // 从方法中返回,返回值为void
Classification of instructions:
- According to the nature of the instruction, it can be mainly divided into four types:
- Stack manipulation instructions, including those that interact with local variables
- Program Flow Control Instructions
- Object manipulation instructions, including method call instructions
- Arithmetic operations and type conversion instructions
Common commands:
- [ Local variable stack instruction ] Load a local variable to the operand stack:
xload
,xload_
(where x is i, l, f, d, a, n is 0 to 3) - [ Constant stack instruction ] Load a constant into the operand stack: bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_ml, iconst, lconst, fconst, dconst
- [ Pop stack and load local variable table instruction ] Store a value from the operand stack to the local variable table: xstore, xstore_ (where x is i, l, f, d, a, n is 0 to 3); xastore (where x is i, 1, f, d, a, b, c, s)
Some of the instruction mnemonics listed above end with angle brackets (such as iload). These instruction mnemonics actually represent a group of instructions (for example, iload represents the instructions iload0, iload1, iload2, and iload3). These groups of instructions are special forms of a general instruction (such as iload) with one operand. For these groups of special instructions, they seem to have no operands and do not need to perform the action of fetching operands, but The operands are all implicit in the instruction.
iload_0: Push the data at index 0 in the local variable table into the operand stack.
iload 0: Push the data at index 0 in the local variable table into the operand stack.
iload 4: Push the data at index 4 in the local variable table into the operand stack.
The meanings expressed by the first two are the same, but iload_0 is equivalent to only the opcode, so it only occupies 1 byte, while iload 0 is composed of the opcode and the operand, and the operand occupies 2 bytes, so it occupies 3 bytes.
For more instructions, please refer to " JVM Assembly Instruction Stack and Local Variable Operation "
3. Summary
This article is just a brief understanding of the bytecode viewing method, the structure of the bytecode file, and how the bytecode runs. The relationship between threads and stack frames is as follows (network diagram, to help understand):
Finally, refer to the calling process between threads and stack frames on the Internet:
Thread Creation -> Thread Stack Creation -> Call Stack Frame A -> Stack Frame A Creation -> Stack Frame A Call Operand Stack -> Stack Frame A Operand Stack Call Stack Frame B -> Stack Frame B Creation -> Stack Frame B calls operand stack -> stack frame B returns -> stack frame B destroys -> stack frame A returns -> stack frame A destroys -> thread stack destroys -> thread destroys
- thread creation
- thread stack creation
- call stack frame A
- Stack frame A is created
- Stack frame A call operand stack
- Stack frame A operand stack call stack frame B
- Stack frame B is created
- Stack frame B call operand stack
- Stack frame B returns
- Stack frame B is destroyed
- Stack frame A returns
- Stack frame A is destroyed
- thread stack destruction
- thread destruction
article citation
https://blog.csdn.net/xidianzxm/article/details/108634553 (operating principle of thread-stack frame illustration/multi-thread stack frame)
https://blog.csdn.net/Mr_YarNell/article/details/118482034 ( Java bytecode (Java bytecode))