字节码
.class文件是按照字节码标准形成的字节码文件。javap是Java class文件分解器,可以反编译,也可以查看java编译器生成的字节码。用于分解class文件。
Java源码
public class InvokeTest {
int i = 0;
public void invokeTest() {
}
public synchronized void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void test2() {
synchronized (this) {
test3();
i++;
}
}
private void test3() {
}
public static void main(String[] args) {
}
}
查看字节码
java命令中我经常使用的命令是javap -p -v 类名,例如:javap -p -v InvokeTest.class,结果如下所示:
Last modified 2025年3月23日; size 1076 bytes Test\$1.class Test.class
SHA-256 checksum 14a3ce22deb3e6be6e9b1ccffdfe19e58c3b821710ae1d916751a71d199d597e
Compiled from "InvokeTest.java"
public class InvokeTest
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #10 // InvokeTest
super_class: #11 // java/lang/Object
interfaces: 0, fields: 1, methods: 6, attributes: 1
Constant pool:
#1 = Methodref #11.#35 // java/lang/Object."<init>":()V
#2 = Fieldref #10.#36 // InvokeTest.i:I
#3 = Long 1000l
#5 = Methodref #37.#38 // java/lang/Thread.sleep:(J)V
#6 = Class #39 // java/lang/InterruptedException
#7 = Class #40 // java/lang/RuntimeException
#8 = Methodref #7.#41 // java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
#9 = Methodref #10.#42 // InvokeTest.test3:()V
#10 = Class #43 // InvokeTest
#11 = Class #44 // java/lang/Object
#12 = Utf8 i
#13 = Utf8 I
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LInvokeTest;
#21 = Utf8 invokeTest
#22 = Utf8 test
#23 = Utf8 e
#24 = Utf8 Ljava/lang/InterruptedException;
#25 = Utf8 StackMapTable
#26 = Utf8 test2
#27 = Class #45 // java/lang/Throwable
#28 = Utf8 test3
#29 = Utf8 main
#30 = Utf8 ([Ljava/lang/String;)V
#31 = Utf8 args
#32 = Utf8 [Ljava/lang/String;
#33 = Utf8 SourceFile
#34 = Utf8 InvokeTest.java
#35 = NameAndType #14:#15 // "<init>":()V
#36 = NameAndType #12:#13 // i:I
#37 = Class #46 // java/lang/Thread
#38 = NameAndType #47:#48 // sleep:(J)V
#39 = Utf8 java/lang/InterruptedException
#40 = Utf8 java/lang/RuntimeException
#41 = NameAndType #14:#49 // "<init>":(Ljava/lang/Throwable;)V
#42 = NameAndType #28:#15 // test3:()V
#43 = Utf8 InvokeTest
#44 = Utf8 java/lang/Object
#45 = Utf8 java/lang/Throwable
#46 = Utf8 java/lang/Thread
#47 = Utf8 sleep
#48 = Utf8 (J)V
#49 = Utf8 (Ljava/lang/Throwable;)V
{
int i;
descriptor: I
flags: (0x0000)
public InvokeTest();
descriptor: ()V
flags: (0x0001) 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: iconst_0
6: putfield #2 // Field i:I
9: return
LineNumberTable:
line 1: 0
line 2: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LInvokeTest;
public void invokeTest();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LInvokeTest;
public synchronized void test();
descriptor: ()V
flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=2, args_size=1
0: ldc2_w #3 // long 1000l
3: invokestatic #5 // Method java/lang/Thread.sleep:(J)V
6: goto 19
9: astore_1
10: new #7 // class java/lang/RuntimeException
13: dup
14: aload_1
15: invokespecial #8 // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
18: athrow
19: return
Exception table:
from to target type
0 6 9 Class java/lang/InterruptedException
LineNumberTable:
line 8: 0
line 11: 6
line 9: 9
line 10: 10
line 12: 19
LocalVariableTable:
Start Length Slot Name Signature
10 9 1 e Ljava/lang/InterruptedException;
0 20 0 this LInvokeTest;
StackMapTable: number_of_entries = 2
frame_type = 73 /* same_locals_1_stack_item */
stack = [ class java/lang/InterruptedException ]
frame_type = 9 /* same */
public void test2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: invokevirtual #9 // Method test3:()V
8: aload_0
9: dup
10: getfield #2 // Field i:I
13: iconst_1
14: iadd
15: putfield #2 // Field i:I
18: aload_1
19: monitorexit
20: goto 28
23: astore_2
24: aload_1
25: monitorexit
26: aload_2
27: athrow
28: return
Exception table:
from to target type
4 20 23 any
23 26 23 any
LineNumberTable:
line 15: 0
line 16: 4
line 17: 8
line 18: 18
line 19: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 this LInvokeTest;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 23
locals = [ class InvokeTest, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
private void test3();
descriptor: ()V
flags: (0x0002) ACC_PRIVATE
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 23: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LInvokeTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 27: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 args [Ljava/lang/String;
}
SourceFile: "InvokeTest.java"
完整解析上面的字节码如下: 1.Last modified 2025年3月23日; size 1076 bytes Test$1.class 这行信息代表最后更改时间。 2. major version: 55,代表JAVA11,52代表JAVA8,53代表JAVA9,54代表JAVA10,以此类推。 3. flags: (0x0021) ACC_PUBLIC, ACC_SUPER代表class是public。 4. super_class: #11 // java/lang/Object,代表父类是Object,#11代表常量池的索引。 5. interfaces: 0, fields: 1, methods: 6, attributes: 1代表实现的接口为0个,field(实力变量)为1个,方法6个,属性1个。 6. 常量池列表中包括class,例如:#11 = Class #44 // java/lang/Object,方法例如:#1 = Methodref #11.#35 // java/lang/Object."":()V,变量例如:#3 = Long 1000l。
7.int i; 描述为I,就是int的描述。
-
public InvokeTest();构造方法解析:描述符为: ()V,无参数返回值为空。标志位为public。代码stack=2, locals=1, args_size=1,操作数栈最大值为2,局部变量为1,参数个数为1(实例方法参数个数加1,静态方法为参数个数)。 0: aload_0 //把实例从局部变量索引的0位置存放到操作数栈的顶部
1: invokespecial #1 // Method java/lang/Object."":()V 调用Object的init方法
4: aload_0 //把实例从局部变量索引的0位置存放到操作数栈的顶部
5: iconst_0 //把int值0放到操作数顶部
6: putfield #2 // Field i:I 把实例属性i设置为0,操作数栈顶部数据分别是0和刚刚构建的实例
7: return //方法返回
9.public synchronized void test()方法描述()V无参数无返回值,标志位包括ACC_SYNCHRONIZED,代表同步方法,多线程同时调用此方法的时候,只能有一个线程能获取锁,方法执行完毕之后,线程会释放锁。
方法调用和返回指令的区别
在字节码指令中,与方法调用相关的指令有如下5种:
- invokestatic:该指令用于调用静态方法。
- invokeinterface:该指令用于调用接口方法。
- invokespecial:该指令用于调用一些需要特殊处理的实例方法,包括实例的初始化方法、私有方法和父类方法。
- invokevirtual:该指令用于调用对象的实例方法。
- invokedynamic:该指令用于在运行时动态解析出调用点限定符所用的方法,并执行该方法。
总结
字节码文件中的信息方法名,变量名,以及方法体的具体实现,不同方法调用之间的异同点,以及方法局部变量和操作数栈之间的转换等详细信息。希望文章对您有所帮助,如有错误,请不吝指出。