深入Java虚拟机读书笔记
前面Java class文件解析1中介绍了一大堆的概念性的东西,下面来讲个例子。
一个简单的例子:
public class TestDemo {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
执行这段代码之后,就会得到一个TestDemo.class文件,进入这个文件目录,执行下面指令:
javap -verbose TestDemo.class
Classfile /TestDemo/out/production/TestDemo/TestDemo.class
Last modified Jan 10, 2016; size 528 bytes
MD5 checksum 889044c6341947fd4c10d64e3daf91df
Compiled from "TestDemo.java"
public class TestDemo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // Hello World!
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // TestDemo
#6 = Class #27 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LTestDemo;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 TestDemo.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 Hello World!
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 TestDemo
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
{
public TestDemo();
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 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestDemo;
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 #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 7: 0
line 8: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "TestDemo.java"
上面的内容就是class文件中的内容,下面我们来进行解析解析。
对照上面这个表格,我们依次来看看各项的内容:
上面对应的就是minor_version、major_version、access_flages这三项。
建议结合Java class文件解析1一起来看就会很清晰了。
上面对应的就是常量池中的内容。以第一个常量池入口为例,我们来看看它们之间的索引关系,其他依次类推。
上面有两个方法,一个是TestDemo的构造函数,一个是main函数。
我们重点来看看main函数:
stack=2,locals=1,args_size=1。
后面对应的就是指令代码code 数组。0 表示 code[]的索引位置,后面的getstatic对应执行命令。
LineNumberTable属性用于调试,它将源码和字节码匹配了起来。line 7: 0这句话
代表该函数字节码 0 那一个操作对应代码的第 7 行。
LocalVariableTable属性用于调试,它用于描述函数执行时的变量信息。Start = 0:表示从 code[]第 0 个字节开始,Length = 9 表示到从 start=0 到 start+9 个字节(不包含第 9
个字节,因为 code 数组一共就 8 个字节),Slot=0 表示这个变量在本地变量表locals中第一个元素。