少年帮|JVM内幕:Java虚拟机详解( 四 )


可以用 javap 查看编译后的 java class 文件字节码 。
如果你编译下面这个简单的类:
package org.jvminternals;public class SimpleClass {public void sayHello() {System.out.println("Hello");}}运行下面的命令 , 就可以得到下面的结果输出: javap -v -p -s -sysinfo -constants classes/org/jvminternals/SimpleClass.class 。
public class org.jvminternals.SimpleClassSourceFile: "SimpleClass.java"minor version: 0major version: 51flags: ACC_PUBLIC, ACC_SUPERConstant pool:#1 = Methodref#6.#17//java/lang/Object."":()V#2 = Fieldref#18.#19//java/lang/System.out:Ljava/io/PrintStream;#3 = String#20//"Hello"#4 = Methodref#21.#22//java/io/PrintStream.println:(Ljava/lang/String;)V#5 = Class#23//org/jvminternals/SimpleClass#6 = Class#24//java/lang/Object#7 = Utf8#8 = Utf8()V#9 = Utf8Code#10 = Utf8LineNumberTable#11 = Utf8LocalVariableTable#12 = Utf8this#13 = Utf8Lorg/jvminternals/SimpleClass;#14 = Utf8sayHello#15 = Utf8SourceFile#16 = Utf8SimpleClass.java#17 = NameAndType#7:#8//"":()V#18 = Class#25//java/lang/System#19 = NameAndType#26:#27//out:Ljava/io/PrintStream;#20 = Utf8Hello#21 = Class#28//java/io/PrintStream#22 = NameAndType#29:#30//println:(Ljava/lang/String;)V#23 = Utf8org/jvminternals/SimpleClass#24 = Utf8java/lang/Object#25 = Utf8java/lang/System#26 = Utf8out#27 = Utf8Ljava/io/PrintStream;#28 = Utf8java/io/PrintStream#29 = Utf8println#30 = Utf8(Ljava/lang/String;)V{public org.jvminternals.SimpleClass();Signature: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1// Method java/lang/Object."":()V4: returnLineNumberTable:line 3: 0LocalVariableTable:StartLengthSlotNameSignature050thisLorg/jvminternals/SimpleClass;public void sayHello();Signature: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: getstatic#2// Field java/lang/System.out:Ljava/io/PrintStream;3: ldc#3// String "Hello"5: invokevirtual#4// Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 6: 0line 7: 8LocalVariableTable:StartLengthSlotNameSignature090thisLorg/jvminternals/SimpleClass;}这个 class 文件展示了三个主要部分:常量池、构造器方法和 sayHello 方法 。

  • 常量池:提供了通常由符号表提供的相同信息 , 详细描述见下文 。
  • 方法:每一个方法包含四个区域 ,
    • 签名和访问标签
    • 字节码
    • LineNumberTable:为调试器提供源码中的每一行对应的字节码信息 。 上面的例子中 , Java 源码里的第 6 行与 sayHello 函数字节码序号 0 相关 , 第 7 行与字节码序号 8 相关 。
    • LocalVariableTable:列出了所有栈帧中的局部变量 。 上面两个例子中 , 唯一的局部变量就是 this 。
这个 class 文件用到下面这些字节码操作符:
aload0这个操作码是aload格式操作码中的一个 。 它们用来把对象引用加载到操作码栈 。表示正在被访问的局部变量数组的位置 , 但只能是0、1、2、3 中的一个 。 还有一些其它类似的操作码用来载入非对象引用的数据 , 如iload, lload, float 和 dload 。 其中 i 表示 int , l 表示 long , f 表示 float , d 表示 double 。 局部变量数组位置大于 3 的局部变量可以用 iload, lload, float, dload 和 aload 载入 。 这些操作码都只需要一个操作数 , 即数组中的位置ldc这个操作码用来将常量从运行时常量池压栈到操作数栈getstatic这个操作码用来把一个静态变量从运行时常量池的静态变量列表中压栈到操作数栈invokespecial, invokevirtual这些操作码属于一组函数调用的操作码 , 包括:invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual 。 在这个 class 文件中 , invokespecial 和 invokevirutal 两个指令都用到了 , 两者的区别是 , invokevirutal 指令调用一个对象的实例方法 , invokespecial 指令调用实例初始化方法、私有方法、父类方法 。 return这个操作码属于ireturn、lreturn、freturn、dreturn、areturn 和 return 操作码组 。 每个操作码返回一种类型的返回值 , 其中 i 表示 int , l 表示 long , f 表示 float , d 表示 double , a 表示 对象引用 。 没有前缀类型字母的 return 表示返回 void