Java源代碼經過編譯生成.class文件字節碼後再被JVM解釋轉化爲目標機器代碼,從而實現一次編寫處處,處處運行("Write Once,Run Anywhere")。字節碼與平臺無關,並且並非只有Java語言編譯爲字節碼文件在虛擬機上運行。java
Class文件是一組以8位字節爲基礎單位的二進制流,各個數據項目嚴格按照順序緊湊地排列在Class文件中。Class文件只有兩種數據類型:無符號數和表。
數組
無符號數屬於基本的數據類型,有u1, u2, u4, u8,分別表明1個字節、2個字節、4個字節和8個字節的無符號數
併發
整個Class文件就是一張表,由如下數據項構成:
this
類型 | 名稱 | 數量 |
---|---|---|
u4 | magic(魔數) | 1 |
u2 | minor_version(次版本號) | 1 |
u2 | major_version(主版本號) | 1 |
u2 | constant_pool_count(常量池容量) | 1 |
cp_info | constant_pool(常量池) | constant_pool_count-1 |
u2 | access_flags(訪問標誌) | 1 |
u2 | this_class(類索引) | 1 |
u2 | super_class(父類索引) | 1 |
u2 | interfaces_count(接口容量) | 1 |
u2 | interfaces(接口) | interfaces_count |
u2 | fields_count(字段容量) | 1 |
field_info | fields(字段) | fields_count |
u2 | methods_count(方法容量) | 1 |
mehtod_info | methods(方法) | method_count |
u2 | attributes_count(屬性容量) | 1 |
attribute | attributes(屬性) | attributes_count |
寫個簡單實體類,javac編譯後,查看其字節碼
源碼編碼
public class Person {
private int age;
public int getAge(){
return age;
}
public static synchronized void work() {
System.out.println("工做");
}
public static void main(String[] args) {
}
}
複製代碼
十六進制Class文件spa
根據上述數據項表格咱們按順序拆分code
魔數站每一個Class文件的頭4個字節,其做用未肯定肯定這個文件是否爲一個能被虛擬機接受的Class文件 示例中CA FE BA BE爲魔數
cdn
魔數後面緊跟着版本號
00 00——次版本號
00 34——主版本號
根據以下:對象
十進制版本號 | 主版本 |
---|---|
jdk1.8 | 52 |
jdk1.7 | 51 |
jdk1.6 | 50 |
jdk1.5 | 49 |
jdk1.4 | 48 |
jdk1.3 | 47 |
jdk1.2 | 46 |
jdk1.1 | 45 |
常量池能夠理解爲Class文件之中的資源倉庫,它是Class文件結構中與其餘項目關聯最多的數據類型。常量池中主要存放兩大類常量:字面量和符號引用.
字面量——接近於Java中的常量概念,eg:final修飾、文本字符串
符號引用——編譯原理概念:類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符
常量池中的每一項常量都是一個表,每種常量都有本身的結構,14種常量含義:blog
類型 | 標誌 | 描述 |
---|---|---|
CONSTANT_utf8_info | 1 | utf-8編碼的字符串 |
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_InterfaceMethoderf_info | 11 | 接口中方法的符號引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的部分符號引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MethodType_info | 16 | 標識方法類型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一個動態方法調用點 |
0×0029轉十進制爲41,表明常量池中有40項常量(容量計數是從1而不是0開始。第0項常量空出來是表達「不引用任何一個常量池項目」)
0A即十進制10,對應表中CONSTANT_Methodref_info,其結構以下:
類型 | 名稱 | 描述 |
u1 | tag | 值爲10 |
u2 | index | 指向聲明方法的類描述符CONSTANT_Class_info的索引項 |
u2 | index | 指向名稱及類型描述符CONSTANT_NameAndType_info的索引項 |
00 29 //constant_pool_count(常量池容量)
#一、0A 0007 001A //CONSTANT_Methodref_info,#7,#26
#二、09 0006 001B //CONSTANT_Fieldref_info,#6,#27
#三、09 001C 001D //CONSTANT_Fieldref_info,#28,#29
#四、08 001E //CONSTANT_String_info,#30
#五、0A 001F 0020 //CONSTANT_Methodref_info,#31,#32
#六、07 0021 //CONSTANT_Class_info,#33
#七、07 0022 //CONSTANT_Class_info,#34
#八、01 0003 61 67 65 //CONSTANT_Utf8_info,3個字節,age
#九、01 0001 49 //CONSTANT_Utf8_info,1個字節,I
#十、01 0006 3C 69 6E 69 74 3E //CONSTANT_Utf8_info,6個字節,
#十一、01 0003 28 29 56 //CONSTANT_Utf8_info,3個字節,()V
#十二、01 0004 43 6F 64 65 //CONSTANT_Utf8_info,4個字節,Code
#1三、01 000F 4C 69 6E 65 4E 75 6D 62 //CONSTANT_Utf8_info,15個字節,LineNumberTable
65 72 54 61 62 6C 65
#1四、01 0012 4C 6F 63 61 6C 56 61 72 //CONSTANT_Utf8_info,18個字節,LocalVariableTable
69 61 62 6C 65 54 61 62
6C 65
#1五、01 0004 74 68 69 73 //CONSTANT_Utf8_info,4個字節,this
#1六、01 0008 4C 50 65 72 73 6F 6E 3B //CONSTANT_Utf8_info,8個字節,LPerson;
#1七、01 0006 67 65 74 41 67 65 //CONSTANT_Utf8_info,6個字節,getAge
#1八、01 0003 28 29 49 //CONSTANT_Utf8_info,3個字節,()I
#1九、01 0004 77 6F 72 6B //CONSTANT_Utf8_info,4個字節,work
#20、01 0004 6D 61 69 6E //CONSTANT_Utf8_info,4個字節,main
#2一、01 0016 28 5B 4C 6A 61 76 61 2F //CONSTANT_Utf8_info,22個字節,([Ljava/lang/String;)V
6C 61 6E 67 2F 53 74 72
69 6E 67 3B 29 56
#2二、01 0004 61 72 67 73 //CONSTANT_Utf8_info,4個字節,args
#2三、01 0013 5B 4C 6A 61 76 61 2F 6C //CONSTANT_Utf8_info,19個字節,[Ljava/lang/String;
61 6E 67 2F 53 74 72 69
6E 67 3B
#2四、01 000A 53 6F 75 72 63 65 46 69 //CONSTANT_Utf8_info,10個字節, SourceFile
6C 65
#2五、01 000B 50 65 72 73 6F 6E 2E 6A //CONSTANT_Utf8_info,11個字節, Person.java
61 76 61
#2六、0C 000A 000B //CONSTANT_NameAndType_info,#10,#11
#2七、0C 0008 0009 //CONSTANT_NameAndType_info,#8,#9
#2八、07 0023 //CONSTANT_Class_info,#35
#2九、0C 0024 0025 //CONSTANT_NameAndType_info,#36,#37
#30、01 0006 E5 B7 A5 E4 BD 9C //CONSTANT_Utf8_info,6個字節,工做
#3一、07 0026 //CONSTANT_Class_info,#38
#3二、0C 0027 0028 //CONSTANT_NameAndType_info,#39,#40
#3三、01 0006 50 65 72 73 6F 6E //CONSTANT_Utf8_info,6個字節,Person
#3四、01 0010 6A 61 76 61 2F 6C 61 6E //CONSTANT_Utf8_info,16個字節,java/lang/Object
67 2F 4F 62 6A 65 63 74
#3五、01 0010 6A 61 76 61 2F 6C 61 6E //CONSTANT_Utf8_info,16個字節, java/lang/System
67 2F 53 79 73 74 65 6D
#3六、01 0003 6F 75 74 //CONSTANT_Utf8_info,3個字節,out
#3七、01 0015 4C 6A 61 76 61 2F 69 6F //CONSTANT_Utf8_info,21個字節, Ljava/io/PrintStream;
2F 50 72 69 6E 74 53 74
72 65 61 6D 3B
#3八、01 0013 6A 61 76 2F 69 6F 2F 50 //CONSTANT_Utf8_info,19個字節,java/io/PrintStream
72 69 6E 74 53 74 72 65
61 6D
#3九、01 0007 70 72 69 6E 74 6C 6E //CONSTANT_Utf8_info,7個字節,println
#40、01 0015 28 4C 6A 61 76 61 2F 6C //CONSTANT_Utf8_info,21個字節, (Ljava/lang/String;)V
61 6E 67 2F 53 74 72 69
6E 67 3B 29 56
複製代碼
也能夠java -verbose分析Class文件字節碼,獲得結果:
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."":()V
#2 = Fieldref #6.#27 // Person.age:I
#3 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#4 = String #30 // 工做
#5 = Methodref #31.#32 // java/io/PrintStream.println:(Ljava/lang/String;)V
#6 = Class #33 // Person
#7 = Class #34 // java/lang/Object
#8 = Utf8 age
#9 = Utf8 I
#10 = Utf8
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 LPerson;
#17 = Utf8 getAge
#18 = Utf8 ()I
#19 = Utf8 work
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 SourceFile
#25 = Utf8 Person.java
#26 = NameAndType #10:#11 // "":()V
#27 = NameAndType #8:#9 // age:I
#28 = Class #35 // java/lang/System
#29 = NameAndType #36:#37 // out:Ljava/io/PrintStream;
#30 = Utf8 工做
#31 = Class #38 // java/io/PrintStream
#32 = NameAndType #39:#40 // println:(Ljava/lang/String;)V
#33 = Utf8 Person
#34 = Utf8 java/lang/Object
#35 = Utf8 java/lang/System
#36 = Utf8 out
#37 = Utf8 Ljava/io/PrintStream;
#38 = Utf8 java/io/PrintStream
#39 = Utf8 println
#40 = Utf8 (Ljava/lang/String;)V
複製代碼
在常量池以後緊接着兩個字節表明訪問標誌,用於識別一些類或者接口層次的訪問信息
具體的標誌位和含義以下:
名稱 | 標誌值 | 含義 |
ACC_PUBLIC | 0×0001 | 是否爲public |
ACC_FINAL | 0x0010 | 是否爲final |
ACC_SUPER | 0x0020 | JDK 1.0.2以後編譯出來的類這個標誌都爲真 |
ACC_INTERFACE | 0x0200 | 是否爲一個接口 |
ACC_ABSTRACT | 0x0400 | 是否爲abstract類型 |
ACC_SUPER | 0x0020 | JDK 1.0.2以後編譯出來的類這個標誌都爲真 |
ACC_SYNTHETIC | 0x1000 | 標識這個類並不是由用戶代碼產生 |
ACC_ANNOTATION | 0x2000 | 是不是註解 |
ACC_ENUM | 0x4000 | 是不是枚舉 |
類索引、父類索引與接口索引(指向常量池)都是u2類型的數據,除了java.lang.Object 以外全部的Java類都有父類,沒有實現接口計數器爲0,本例:
0006 //this_class Person
0007 //super_class java/lang/Object
0000 //沒有實現結構故0
複製代碼
字段表用於描述類和接口中聲明的變量。字段包括類級變量和實例級變量,可是不包括方法中的變量。字段信息:字段的做用域,public/private/protected 實例變量仍是類變量,static 可變性,final 併發可見性, volatile 能否被序列化, transient,字段數據類型(基本類型,對象,數組),字段名稱
字段表結構:
類型 | 名稱 | 數量 |
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
0001 //fields_count 字段容量即1個字段
0002 //訪問標誌 private
0008 //常量池第8項,即age
0009 //字段描述符,常量池第9項,即I
0000 //attribute_count
複製代碼
Class文件存儲格式中對方法的描述與對字段的描述幾乎採用徹底一致的方式。
方法表結構:
類型 | 名稱 | 數量 |
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
0004 //方法容量,即4個方法:實例構造器、getAge()、work以及main方法 0001 //方法訪問標誌,public 000A //常量池第10項, 000B //方法描述常量池第11個,()V,沒返回值 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 0000002F //屬性表長度 47 ... 47個字節後複製代碼0001 //方法訪問標誌,public 0011 //常量池第17項,getAge 0012 //方法描述常量池第18個,()I 返回int型 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 0000002F //屬性表長度 47 ... 47個字節後 0029 // ACC_PUBLIC,ACC_STATIC,ACC_SYNCHRONIZED 0×0001|0×0008|0×0020 0013 //常量池第19項,work 000B //方法描述常量池第11個,()V,沒返回值 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 00000025 //屬性表長度 37 ...37個字節後 0009 //ACC_PUBLIC, ACC_STATIC 0×0001|0×0008 0014 //常量池第20項,main 0015 //方法描述常量池第21項,([Ljava/lang/String;)V String數組形參,無返回類型方法 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 0000002B //屬性表長度 43 複製代碼複製代碼0001 //方法訪問標誌,public 0011 //常量池第17項,getAge 0012 //方法描述常量池第18個,()I 返回int型 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 0000002F //屬性表長度 47 ... 47個字節後 0029 // ACC_PUBLIC,ACC_STATIC,ACC_SYNCHRONIZED 0×0001|0×0008|0×0020 0013 //常量池第19項,work 000B //方法描述常量池第11個,()V,沒返回值 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 00000025 //屬性表長度 37 ...37個字節後 0009 //ACC_PUBLIC, ACC_STATIC 0×0001|0×0008 0014 //常量池第20項,main 0015 //方法描述常量池第21項,([Ljava/lang/String;)V String數組形參,無返回類型方法 0001 //attribute_count 000C //常量池第12項,Code屬性表,存放方法裏的Java代碼 0000002B //屬性表長度 43 複製代碼
對於本例:
000C //常量池第12項,Code屬性
0000002F //Code屬性表長度 47
0001 //max_stack 操做數棧深度最大值 1
0001 //max_locals 局部變量存儲
00000005 //code_length 字節碼長度
2A B7 00 01 B1 //字節碼指令
0000 //exception_table_length
0002 //attributes_count 2個屬性
000D //常量池第13項, LineNumberTable屬性
00000006 //LineNumberTable屬性表長度
0001 //line_number_table_length
0000 //start_pc 字節碼行號
0001 //line_number Java源碼行號
000E //常量池第14項,LocalVariableTable屬性
0000000C //attribute_length
0001 //local_variable_table_length
0000 //start_pc 這個局部變量的生命週期開始的字節碼偏移量
0005 //局部變量做用範圍覆蓋的長度
000F //name_index 局部變量名稱 常量池第15項,this
0010 //descriptor_index 局部變量描述 常量池第16項,LPerson;
0000 //這個局部變量在棧幀局部變量表中Slot的位置
複製代碼
000C //常量池第12項,Code屬性
0000002F //attribute_length
0001 //max_stack 操做數棧深度最大值 1
0001 //max_locals 局部變量存儲
00000005 //code_length 字節碼長度
2A B4 00 02 AC //字節碼指令
0000 //exception_table_length
0002 //attributes_count 2個屬性
000D //常量池第13項, LineNumberTable屬性
00000006 //LineNumberTable屬性表長度
0001 //line_number_table_length
0000 //start_pc 字節碼行號
0006 //line_number Java源碼行號
000E //常量池第14項,LocalVariableTable屬性
0000000C //attribute_length
0001 //local_variable_table_length
0000 //start_pc 這個局部變量的生命週期開始的字節碼偏移量
0005 //局部變量做用範圍覆蓋的長度
000F //name_index 局部變量名稱 常量池第15項,this
0010 //descriptor_index 局部變量描述 常量池第16項,LPerson;
0000 //這個局部變量在棧幀局部變量表中Slot的位置
複製代碼
000C //常量池第12項,Code屬性
00000025 //attribute_length
0002 //max_stack 操做數棧深度最大值 2
0000 //max_locals 局部變量存儲
00000009 //code_length 字節碼長度
B2 00 03 12 04 B6 00 05 B1 //字節碼指令
0000 //exception_table_length
0001 //attributes_count 1個屬性
000D //常量池第13項, LineNumberTable屬性
0000000A //LineNumberTable屬性表長度
0002 //line_number_table_length 2個line_number_info
0000 //start_pc 字節碼行號
000A //line_number Java源碼行號
0008 //start_pc 字節碼行號
000B //line_number Java源碼行號
複製代碼
000C //常量池第12項,Code屬性
0000002B //attribute_length
0000 //max_stack 操做數棧深度最大值 0
0001 //max_locals 局部變量存儲
00000001 //code_length 字節碼長度
B1 //字節碼指令
0000 //exception_table_length
0002 //attributes_count 2個屬性
000D //常量池第13項, LineNumberTable屬性
00000006 //LineNumberTable屬性表長度
0001 //line_number_table_length 1個line_number_info
0000 //start_pc 字節碼行號
000F //line_number Java源碼行號
000E //常量池第14項,LocalVariableTable屬性
0000000C //attribute_length
0001 //local_variable_table_length
0000 //start_pc 這個局部變量的生命週期開始的字節碼偏移量
0001 //局部變量做用範圍覆蓋的長度
0016 //name_index 局部變量名稱 常量池第22項,args
0017 //descriptor_index 局部變量描述 常量池第23項,[Ljava/lang/String;
0000 //這個局部變量在棧幀局部變量表中Slot的位置
複製代碼
本篇作了一個小小的嘗試,按照數據項表格一一解析,感興趣的同窗能夠讀下《深刻理解Java虛擬機》這本聖書。
《深刻理解Java虛擬機》