本文不对字节码中的每一个指令进行解析,只对几个实例进行解析,以此来了解java文件编译后生产的class字节码。实例中所有所有代码均可直接使用,建议自己重新实验一下。JVM字节码在通常开发情况下没有用处,但是是一种无侵入监控方法(动态字节码技术)、匪夷所思的BUG调试的重要技术手段。
一、环境
(1)JDK1.7
(2)javac Test.java 生产Test.class
(3)javap -c Test.class > test.txt 输出JVM字节码
二、字节码实例
字符串
StringAppend类为例
源码如下:
package com.owl.zookeeper.string;
public class StringAppend {
public static void main(String[] args) {
String a = "1";
String b = "2" + a + "3";
System.out.println(b);
}
}
字节码如下:
Compiled from "StringAppend.java"
public class com.owl.zookeeper.string.StringAppend {
public com.owl.zookeeper.string.StringAppend();
Code:
0: aload_0 //将this入栈
1: invokespecial #1 // Method java/lang/Object."<init>":()V //调用Object的构造方法
4: return //返回void
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 1 解释:LDC cst:将常量池偏移量为cst的值入栈,譬如LDC #12,在操作栈中会占用1个字长
2: astore_1 解释:将栈顶的String 1赋值给第一个变量a
3: new #3 // class java/lang/StringBuilder 解释:创建对象,并将该对象入栈顶
6: dup 解释:复制栈顶数据StringBuilder。因为方法调用会弹出参数(这里是Object对象),因此需要上面的dup指令,保证在调用构造函数之后栈顶上还是 Object对象的引用,很多种情况下dup指令都是为这个目的而存在的。
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 解释:调用StringBuilder构造方法
10: ldc #5 // String 2 解释:将String 2压入栈顶
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解释:栈顶2出栈,作为方法入参,调用java/lang/StringBuilder.append()方法
15: aload_1 解释:将变量a的1入栈
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解释:将变量a的1出栈,作为方法入参,调用java/lang/StringBuilder.append()方法
19: ldc #7 // String 3 解释:将常量池中的字符串3入栈
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解释:将字符串3出栈,作为方法入参,调用java/lang/StringBuilder.append()方法
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 解释:调用StringBuilder.toString
27: astore_2 解释:将返回值保存在变量b
28: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 解释:获得静态变量,System.out
31: aload_2 解释:变量2入栈
32: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 解释:变量2出栈,作为println的方法
35: return 解释:返回
循环
StringForAppend类为例
源码如下:
public class StringForAppend {
public static void main(String[] args) {
String a = "";
for(int i = 0; i < 3; i ++) {
a += "1";
}
System.out.println(a);
}
}
字节码如下:
Compiled from "StringForAppend.java"
public class com.owl.zookeeper.string.StringForAppend {
public com.owl.zookeeper.string.StringForAppend();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 解释:将常量空字符串入栈
2: astore_1 解释:将空字符串赋值给a
3: iconst_0 解释:将常量integer 0入栈
4: istore_2 解释:出栈0,赋值给变量0
5: iload_2 解释:将变量i=0,入栈
6: iconst_3 解释:将integer 3入栈
7: if_icmpge 36 解释:比较栈顶两个元素,如果相等则跳转到36
10: new #3 // class java/lang/StringBuilder 创建StringBuilder,并入栈
13: dup 解释:复制栈顶
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 解释:出栈调用StringBuilder的构造方法
17: aload_1 解释:入栈空字符串
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 出栈空字符串,出栈StringBuilder,执行append方法
21: ldc #6 // String 1 解释:常量1入栈
23: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 出栈字符串1,调用append方法
26: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 出栈StringBuilder,调用append方法,将结果入栈
29: astore_1 解释:出栈""+1,保存到变量a
30: iinc 2, 1 解释:将变量i,递增1
33: goto 5 解释:跳转到步骤5
36: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;解释:获得对象PrintStream,入栈
39: aload_1 解释:入栈变量a
40: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 出栈变量a,出栈PrintStream,调用println方法
43: return 解释:返回
判断
StringIf类为例
源码如下:
public class StringIf {
public static void main(String[] args) {
String a = "123";
if("123" == (a)) {
System.out.println(a);
}else {
String b = a + "!";
System.out.println(b);
}
}
}
字节码如下:
Compiled from "StringIf.java"
public class com.owl.zookeeper.string.StringIf {
public com.owl.zookeeper.string.StringIf();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 123 解释:将常量123入栈
2: astore_1 解释:出栈123,赋值给变量a
3: ldc #2 // String 123 解释:将常量123入栈
5: aload_1 解释:入栈123
6: if_acmpne 19 解释:比较栈顶两个元素,如果符合跳转到指定位置
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;解释:获得System.out对象,入栈
12: aload_1 解释:入栈123
13: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 解释:出栈参数,出栈调用方法对象
16: goto 46 解释:跳转到结束
19: new #5 // class java/lang/StringBuilder 创建一个 解释:创建一个StringBuilder对象,并入栈
22: dup 解释:复制栈顶数据,并入栈
23: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 解释:出栈并调用构造方法
26: aload_1 解释:入栈123
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;解释:出栈123,出栈StringBUilder,调用方法,入栈StirngBuilder
30: ldc #8 // String ! 解释:入栈常量!
32: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;解释:出栈!,出栈StringBuilder,调用append(!),入栈StirngBuilder
35: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 解释:出栈StringBuilder,调用toString()
38: astore_2 解释:出栈123!,赋值给变量b
39: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 解释:获得PrintStream,并入栈
42: aload_2 解释:入栈123!
43: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 出栈2个栈位,调用println
46: return 解释:返回