JVM Class文件详解
.java 文件
public class JVMClassInfo {
public void sayHello(){
System.out.println("hello,world!");
}
}
执行Javap -verbose /你的文件路径/JVMClassInfo.class
public class com.jvm.JVMClassInfo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#17 // java/lang/Object."<init>":()V
#2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #20 // hello,world!
#4 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #23 // com/jvm/JVMClassInfo
#6 = Class #24 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/jvm/JVMClassInfo;
#14 = Utf8 sayHello
#15 = Utf8 SourceFile
#16 = Utf8 JVMClassInfo.java
#17 = NameAndType #7:#8 // "<init>":()V
#18 = Class #25 // java/lang/System
#19 = NameAndType #26:#27 // out:Ljava/io/PrintStream;
#20 = Utf8 hello,world!
#21 = Class #28 // java/io/PrintStream
#22 = NameAndType #29:#30 // println:(Ljava/lang/String;)V
#23 = Utf8 com/jvm/JVMClassInfo
#24 = Utf8 java/lang/Object
#25 = Utf8 java/lang/System
#26 = Utf8 out
#27 = Utf8 Ljava/io/PrintStream;
#28 = Utf8 java/io/PrintStream
#29 = Utf8 println
#30 = Utf8 (Ljava/lang/String;)V
{
public com.jvm.JVMClassInfo();
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/jvm/JVMClassInfo;
public void sayHello();
descriptor: ()V
flags: ACC_PUBLIC
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 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/jvm/JVMClassInfo;
}
主要是 jdk的信息、常量池、以及class文件的字节码样式。
idea 可以安装一个 jclasslib Bytecode viewer 插件来查看字节码信息。

用 Javap 看字节码清楚点,用插件看整个class文件结构更清晰。上面的图中少了一个class信息:MagicNumber。可以使用十六进制的工具打开class文件看class文件的十六进制。

一个class文件分为:MagicNumber,Version,Constant_pool,Access_flag,This_class,Super_class,Interfaces,Fields,Methods ,Attributes十个部分。
-
MagicNumber:class文件开头用4个字节长度表示的值为
CA FE BA BE,Java虚拟机用此来验证一个文件是否为class文件,如果不是,Java虚拟机将拒绝加载此class -
Version:分为Minor Version和Major Version,表示当前的jdk版本号,次版本号和主版本号。跟在MagicNumber后面,
00 00:次版本号,对应的十进制是 0 ;00 34: 主版本号,对应的十进制是 52 。在我们当前分析的环境中代表的是jdk1.8。 -
Constant_pool:常量池,
00 1F两个字节表示常量池的数量,由于常量池从1开始算,所以count为30(31-1)。常量池里面放的是字面常量和符号引用,字面常量主要包含文本串以及被声明为final的常量等;符号引用包含类和接口的全局限定名、字段的名称和描述、方法的名称和描述符。常量的结构以常量的类型开始,接着是数据。类似于下面的结构:
cp_info{ u1 tag; u1 info[]; }tag对应的是常量对应的类型。
Constant Type Tag CONSTANT_Utf8_info 1 CONSTANT_Integer_info 3 CONSTANT_Float_info 4 CONSTANT_Long_info 5 CONSTANT_Double_info 6 CONSTANT_Class_info 7 CONSTANT_String_info 8 CONSTANT_Fieldref_info 9 CONSTANT_Methodref_info 10 CONSTANT_InterfaceMethodref_info 11 CONSTANT_NameAndType_info 12 CONSTANT_MethodHandle_info 15 CONSTANT_MethodType_info 16 CONSTANT_InvokeDynamic_info 18 CONSTANT_xxx结构
CONSTANT_Methodref_info{ u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_Fieldref_info{ u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_Class_info{ u1 tag; u2 name_index; } CONSTANT_Utf8_info{ u1 tag; u2 length; u1 bytes[length]; } CONSTANT_NameAndType_info{ u1 tag; u2 name_indx; u2 descriptor_index; }分析下常量池数量后面的常量池信息。
0A—>10 表示的是 CONSTANT_Methodref_info ,所以有5个字节0A 00 06 00 11来表示,00 06代表的是 class_index , 即 方法所属class信息的索引在常量池中的第6个,00 11代表的是 name_and_type_index ,即方法的描述符位于常量池中的第17个,通过上文中提到的插件可以轻易的看出表示的信息是什么。

后面的信息就依据此方法分析就可以了,这里就不多赘述了。
-
Access_flag:类或接口修饰符,访问权限。
ACC_PUBLIC/ACC_FINAL/ACC_SUPER/ACC_INTERFACE/ACC_ABSTRACT/ACC_SYNTHETIC/ACC_ANNOTATION/ACC_ENUM/...
常量池之后是access_flag,两个字节表示。

图中光标的位置为access_flag的起始位置,00 21来表示。 0x0021 = 0x0001 | 0x0020,表示当前class的access_flag是ACC_PUBLIC|ACC_SUPER。
-
This_class : 保存了当前类的全局限定名在常量池的索引,两个字节表示,
00 05这里指向第5个常量,即 -

-
Super_class: 保存了当前类的父类的全局限定名在常量池里的索引,两个字节表示,
00 06这里指向第6个常量,即

-
Interfaces: 保存了当前类实现的接口列表,包含两个部分:interfaces_count、interfaces[interfaces_count]
-
fields: 保存了当前类的成员列表,包含两个部分:fields_count、fields[fields_count]
field_info{ u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } -
methods: 保存了当前类的方法列表,包含两个部分:methods_count、methods[methods_count]
method_info{ u2 access_flags; // 00 01 ACC_PUBLIC u2 name_index; // 00 07 <init> u2 descriptor_index; //00 08 V() u2 attributes_count; //00 01 attribute info attributes[attributes_count]; }因为当前类中既没有interfaces,也没有fields。所以 method 从
00 02开始,表示有两个方法。 -
attributes: class文件的附加属性。包括 Code、LineNumberTable、LocalVariableTable、SourceFile等
Thanks