JDK1.8 CLASS文件结构(参考深入理解JAVA虚拟机)
本部分分析CLASS文件中各部分意义JAVA代码package com.memory.javaclass;public class TestClass {private int m;public int inc() {return m + 1;}public static void main(String[] args...
本部分分析CLASS文件中各部分意义
JAVA代码
package com.memory.javaclass;
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
public static void main(String[] args) {
}
}
生成class文件用16位编辑器打开后
00000000h: CA FE BA BE 00 00 00 34 00 1A 0A 00 04 00 16 09 ; 漱壕...4........
00000010h: 00 03 00 17 07 00 18 07 00 19 01 00 01 6D 01 00 ; .............m..
00000020h: 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 ; .I...<init>...()
00000030h: 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E ; V...Code...LineN
00000040h: 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 ; umberTable...Loc
00000050h: 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 ; alVariableTable.
00000060h: 00 04 74 68 69 73 01 00 20 4C 63 6F 6D 2F 6D 65 ; ..this.. Lcom/me
00000070h: 6D 6F 72 79 2F 6A 61 76 61 63 6C 61 73 73 2F 54 ; mory/javaclass/T
00000080h: 65 73 74 43 6C 61 73 73 3B 01 00 03 69 6E 63 01 ; estClass;...inc.
00000090h: 00 03 28 29 49 01 00 04 6D 61 69 6E 01 00 16 28 ; ..()I...main...(
000000a0h: 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 ; [Ljava/lang/Stri
000000b0h: 6E 67 3B 29 56 01 00 04 61 72 67 73 01 00 13 5B ; ng;)V...args...[
000000c0h: 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E ; Ljava/lang/Strin
000000d0h: 67 3B 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 ; g;...SourceFile.
000000e0h: 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61 ; ..TestClass.java
000000f0h: 0C 00 07 00 08 0C 00 05 00 06 01 00 1E 63 6F 6D ; .............com
00000100h: 2F 6D 65 6D 6F 72 79 2F 6A 61 76 61 63 6C 61 73 ; /memory/javaclas
00000110h: 73 2F 54 65 73 74 43 6C 61 73 73 01 00 10 6A 61 ; s/TestClass...ja
00000120h: 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 00 21 ; va/lang/Object.!
00000130h: 00 03 00 04 00 00 00 01 00 02 00 05 00 06 00 00 ; ................
00000140h: 00 03 00 01 00 07 00 08 00 01 00 09 00 00 00 2F ; .............../
00000150h: 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 ; ........*?.?..
00000160h: 02 00 0A 00 00 00 06 00 01 00 00 00 03 00 0B 00 ; ................
00000170h: 00 00 0C 00 01 00 00 00 05 00 0C 00 0D 00 00 00 ; ................
00000180h: 01 00 0E 00 0F 00 01 00 09 00 00 00 31 00 02 00 ; ............1...
00000190h: 01 00 00 00 07 2A B4 00 02 04 60 AC 00 00 00 02 ; .....*?..`?...
000001a0h: 00 0A 00 00 00 06 00 01 00 00 00 08 00 0B 00 00 ; ................
000001b0h: 00 0C 00 01 00 00 00 07 00 0C 00 0D 00 00 00 09 ; ................
000001c0h: 00 10 00 11 00 01 00 09 00 00 00 2B 00 00 00 01 ; ...........+....
000001d0h: 00 00 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 ; ....?..........
000001e0h: 01 00 00 00 0D 00 0B 00 00 00 0C 00 01 00 00 00 ; ................
000001f0h: 01 00 12 00 13 00 00 00 01 00 14 00 00 00 02 00 ; ................
00000200h: 15 ; .
然后进行分析
- 每个Class文件的头4个字节称为魔数,它的唯一的作用是确定这个文件是否为一个能被虚拟机接受的Class文件。CA FE BA BE就是Class文件的魔数。
- 紧接着魔数的4个字节存储的是Class的版本号:第5和第6个字节是次版本号,第7和第8个字节是主版本号。Java版本号从45开始,高版本向下兼容,此部分是00 00 00 34。
- 紧接着主次版本号之后的是常量池入口。由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数器。这个容量计数器是从1而不是0开始的,00 1A转成十进制为26代表有25项常量,索引值范围为1~25。
- Class文件结构中只有常量池的容量计数是从1开始,对于其他集合的类型,包括接口索引集合、字段表集合、方法、标记和等容量计数都是从0开始。
- 常量池主要存放两大类常量:字面量和符号引用。
字面量:文本字符串、生命为final的常量值等。
符号引用:
类和接口的全限定名
字段的名称和描述符
方法的名称和描述符 - 常量池每一项常量都是一个表
7. 0x0A为十进制的10,根据定义10代表CONSTANT_Methodref_info,由于每种类型不同,所以还需要根据结构表进行查找,u1、u2、u4代表占用几个字节。所以根据表2,CONSTANT_Methodref_info占用5个字节,所以0x09为下一个常量的开始,其他以此类推。
8. 可以通过javap -verbose对TestClass.class的字节码内容进行输出(如何使用可以参考IDEA->Setting->Tools->External Tools进行配置)
Classfile /D:/MyWorkSpace/JavaHotSpot/target/classes/com/memory/javaclass/TestClass.class
Last modified 2018-8-22; size 513 bytes
MD5 checksum 7986017c7bbe4f564d1ba33df52d42ec
Compiled from "TestClass.java"
public class com.memory.javaclass.TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#22 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#23 // com/memory/javaclass/TestClass.m:I
#3 = Class #24 // com/memory/javaclass/TestClass
#4 = Class #25 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/memory/javaclass/TestClass;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 args
#19 = Utf8 [Ljava/lang/String;
#20 = Utf8 SourceFile
#21 = Utf8 TestClass.java
#22 = NameAndType #7:#8 // "<init>":()V
#23 = NameAndType #5:#6 // m:I
#24 = Utf8 com/memory/javaclass/TestClass
#25 = Utf8 java/lang/Object
{
public com.memory.javaclass.TestClass();
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/memory/javaclass/TestClass;
public int inc();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/memory/javaclass/TestClass;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 13: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 args [Ljava/lang/String;
}
SourceFile: "TestClass.java"
Methodref代表类型
#4代表类型长度
#22代表与#22行关联
9.在常量池结束后,紧接着的两个字节代表访问标志
标志名称 标志值 含义
ACC_PUBLIC 0x00 01 是否为Public类型
ACC_FINAL 0x00 10 是否被声明为final,只有类可以设置
ACC_SUPER 0x00 20 是否允许使用invokespecial字节码指令的新语义.
ACC_INTERFACE 0x02 00 标志这是一个接口
ACC_ABSTRACT 0x04 00 是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假
ACC_SYNTHETIC 0x10 00 标志这个类并非由用户代码产生
ACC_ANNOTATION 0x20 00 标志这是一个注解
ACC_ENUM 0x40 00 标志这是一个枚举
10.访问标志后是类索引、父类索引和接口索引,类索引与父类索引是u2类型,接口索引是一组u2类型,除了java/lang/Object外,其他类都有父类索引#25 = Utf8 java/lang/Object代表父类索引
11、其他还有字段表集合、方法表集合、属性表集合
更多推荐
所有评论(0)