一個(gè)Java程序是怎樣運(yùn)行起來(lái)的【class解析全過(guò)程】
首先編寫(xiě)一測(cè)試程序
public class Test {
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
執(zhí)行javac Test.java 得到Test.class文件(編譯過(guò)程有點(diǎn)復(fù)雜,這里先不看)
執(zhí)行java Test,控制臺(tái)輸出"test",想要弄清楚java程序是怎么運(yùn)行起來(lái)首先得了解清楚class文件
看下Test.class里究竟是什么東西,class文件的內(nèi)容如下:
上圖中都是以16進(jìn)制表示,接下來(lái)挨個(gè)分析其中的內(nèi)容表示什么意思。class文件中存儲(chǔ)的數(shù)據(jù)可以參考下表:
1、magic 魔數(shù)
CA FE BA BE
魔數(shù),確定該文件是否是虛擬機(jī)可以接受的文件
2、class文件版本信息
00 00 00 33
class文件的版本號(hào),51表示jdk1.7.0
3、常量池
3.1常量池入口
00 1D
常量池?cái)?shù)量為29-1=28,每個(gè)類(lèi)只有一個(gè)常量池
常量池中放了字符串,常量值,類(lèi)名稱(chēng),字段名,方法名等,反編譯下Test.class,看看常量池中存放了哪些東西
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // test
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // Test
#6 = Class #22 // java/lang/Object
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Test.java
#15 = NameAndType #7:#8 // "
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 test
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 Test
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
常量池中的項(xiàng)目類(lèi)型有:
CONSTANT_Utf8_info ?????tag標(biāo)志位為1, ??UTF-8編碼的字符串,比如類(lèi)或接口的全限定名,參數(shù)名等
CONSTANT_Integer_info ?tag標(biāo)志位為3, int整型字面量
CONSTANT_Float_info ????tag標(biāo)志位為4, float浮點(diǎn)型字面量
CONSTANT_Long_info ????tag標(biāo)志位為5, long長(zhǎng)整形字面量
CONSTANT_Double_info ?tag標(biāo)志位為6, double雙精度字面量
CONSTANT_Class_info ???tag標(biāo)志位為7, 類(lèi)或接口的符號(hào)引用,指向包含字符串字面值的CONSTANT_Utf8表
CONSTANT_String_info ???tag標(biāo)志位為8,字符串類(lèi)型的字面量,指向包含字符串字面值的CONSTANT_Utf8表
CONSTANT_Fieldref_info ?tag標(biāo)志位為9, ?字段的符號(hào)引用,指向包含該字段所屬類(lèi)名的CONSTANT_Utf8表
CONSTANT_Methodref_info ?tag標(biāo)志位為10,類(lèi)中方法的符號(hào)引用,指向包含該方法所屬類(lèi)型的CONSTANT_Utf8表
CONSTANT_InterfaceMethodref_info tag標(biāo)志位為11, 接口中方法的符號(hào)引用
CONSTANT_NameAndType_info ?tag 標(biāo)志位為12,字段和方法的名稱(chēng)以及類(lèi)型的符號(hào)引用
3.2常量池內(nèi)容
接上,繼續(xù)分析class中的內(nèi)容,參照 jvm官方文檔 ,看下常量池中究竟是什么東西
常量池1-----0A 00 06 00 0F ??//
1,0A---tag為10,表示第一個(gè)常量類(lèi)型為CONSTANT_Methodref,參照官方文檔,CONSTANT_Methodref的結(jié)構(gòu)為
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
所以后面跟了4個(gè)字節(jié)
2,00 06---聲明當(dāng)前方法類(lèi)描述符索引值為6 ????// ?java/lang/Object
3,00 0F---當(dāng)前方法的名稱(chēng)和類(lèi)型索引值為15 ?// ?"
所以,結(jié)合上文中反編譯出的內(nèi)容來(lái)看,這幾個(gè)16進(jìn)制翻譯過(guò)來(lái)正好是
#1 = Methodref #6.#15 // java/lang/Object."
常量池2----09 00 10 00 11
1,09---tag為9,類(lèi)型為CONSTANT_Fieldref
2,00 10---聲明當(dāng)前方法類(lèi)描述符索引值為16 // java/lang/System
3,00 11---字段描述符的名稱(chēng)和類(lèi)型索引值為17 // ?out:Ljava/io/PrintStream;
這幾個(gè)16進(jìn)制翻譯過(guò)來(lái)正好是
#2 = Fieldref ? ? ? ? ? #16.#17 ? ? ? ?// ?java/lang/System.out:Ljava/io/PrintStream;
常量池3---08 00 12
1,08---tag為8,類(lèi)型為CONSTANT_String,根據(jù)官方文檔,其結(jié)構(gòu)為
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
所以后面跟了兩個(gè)字節(jié)
2,00 12---聲明當(dāng)前String值所在的索引值為18
當(dāng)前16進(jìn)制翻譯過(guò)來(lái),表示
#3 = String #18 // test
常量池4---0A ?00 13 00 14,對(duì)照著上面的分析,
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
常量池5---07 00 15
1,07---tag為7,類(lèi)型為CONSTANT_Class,根據(jù)官方文檔,其結(jié)構(gòu)為
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
2,00 15----當(dāng)前類(lèi)名稱(chēng)的索引值為21
#5 = Class #21 // Test
常量池6---07 00 16,對(duì)照上面的分析
#6 = Class #22 // java/lang/Object
常量池7---01 00 06 3C 69 6E 69 74 3E
1,01---tag為1,類(lèi)型為CONSTANT_Utf8,根據(jù)官方文檔
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
2,06---表示字符串的長(zhǎng)度為6
3,3C 69 6E 69 74 3E ---字符串
#7 = Utf8
常量池8---01 00 03 28 29 56
常量池9---01 00 04 43 6F 64 65
常量池10---01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65
常量池11---01 00 04 6D 61 69 6E
常量池12---01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56
常量池13---01 00 0A 53 6F 75 72 63 65 46 69 6C 65
常量池14---01 00 09 54 65 73 74 2E 6A 61 76 61
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Test.java
常量池15---0C 00 07 00 08
1,0C---tag為11,類(lèi)型為CONSTANT_NameAndType,參照jvm官方文檔,其結(jié)構(gòu)為
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
2,00 07---該字段或方法名稱(chēng)常量索引值為7,即
#7 = Utf8
3,00 08---該字段或方法描述符常量索引值為8 ,即
#8 = Utf8 ()V
常量池16---07 00 17
常量池17---0C 00 18 00 19
常量池18---01 00 04 74 65 73 74
常量池19---07 00 1A
常量池20---0C 00 1B 00 1C
常量池21---01 00 04 54 65 73 74
常量池22---01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
常量池23---01 00 10 6A 61 76 6A 2F 6C 61 6E 67 2F 53 79 73 74 65 6D
常量池24---01 00 03 6F 75 74
常量池25---01 00 15 4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B
常量池26---01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D
常量池27---01 00 07 70 72 69 6E 74 6C 6E
常量池28---01 00 15 28 4 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 test
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 Test
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
到此常量池結(jié)束
4、訪問(wèn)標(biāo)志access_flags
00 21----Test類(lèi)的訪問(wèn)標(biāo)志,參照官方文檔,訪問(wèn)標(biāo)志有
0x0021 = 0x0020|0x0001,即ACC_PUBLIC和ACC_SUPER為真,ACC_PUBLIC好理解,ACC_SUPER這是什么鬼,翻看官方文檔,原文如下:
The?ACC_SUPER?flag indicates which of two alternative semantics is to be expressed by the?invokespecial?instruction (§invokespecial) if it appears in this class. Compilers to the instruction set of the Java Virtual Machine should set the?ACC_SUPER?flag.
The?ACC_SUPER?flag exists for backward compatibility with code compiled by older compilers for the Java programming language. In Oracle’s JDK prior to release 1.0.2, the compiler generated?ClassFile?access_flags?in which the flag now representing?ACC_SUPER?had no assigned meaning, and Oracle's Java Virtual Machine implementation ignored the flag if it was set.
為了兼容之前的jdk版本,在jdk1.0.2之后這個(gè)編譯出來(lái)的為真
5,類(lèi)索引,父類(lèi)索引,接口索引
接下來(lái)就是類(lèi)索引,父類(lèi)索引,接口索引
00 05------類(lèi)索引值為#5
#5 = Class #21 // Test
00 06-----父類(lèi)索引值為#6
#6 = Class #22 // java/lang/Object
00 00----類(lèi)沒(méi)有實(shí)現(xiàn)接口,接口數(shù)為0,所以后面沒(méi)有接口信息
6、字段
00 00----當(dāng)前類(lèi)有0個(gè)字段
7、方法,指令
00 02----當(dāng)前類(lèi)有兩個(gè)方法,參照官方文檔,方法的結(jié)構(gòu)如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
方法1:00 01 00 07 00 08 00 01
----00 01:access_flags=0x0001=ACC_PUBLIC,方法的訪問(wèn)標(biāo)志如下表:
---00 07:name_index=#7----->#7 = Utf8 ??????????????
---00 08:descriptor_index=#8------>#8 = Utf8 ??????????????()V
---00 01:attributes_count=1,所以緊隨其后就是attribute_info部分,根據(jù)官方文檔,其結(jié)構(gòu)如下:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
00 09 00 00 00 1D 00 01 00 01 00 00 00 05 ??//非指令部分
---00 09:attribute_name_index=#9---------->#9 = Utf8 ??????????????Code
---00 00 00 1D:attribute_length=29,所以整個(gè)屬性表的長(zhǎng)度為29+6=35,見(jiàn)官方文檔說(shuō)明:The value of the?attribute_length?item indicates the length of the attribute, excluding the initial six bytes.
---00 01:max_stack=1
---00 01:max_locals=1
---00 00 00 05:code_length=5
緊接著就是方法1的指令部分:
2A B7 00 01 B1
---2A:aload_0 ,
---B7 00 01 ->invokespecial #1,調(diào)用超類(lèi)構(gòu)造方法
---B1--->return
方法1的Exception:
00 00:方法沒(méi)有throw異常
方法1的attribute count:
00 01://方法1最后有一個(gè)屬性塊,其結(jié)構(gòu)如下:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
00 0A 00 00 00 06 00 01
00 00 00 01
---00 0A:attribute_name_index=#10---->#10 = Utf8 ??????????????LineNumberTable
---00 00 00 06:attribute_lenght=6
---00 01:line_number_table_length=1,表示這個(gè)LineNumberTable中有一條記錄
---00 00 00 01:表示Test.java的第一行代碼對(duì)應(yīng)指令0--->0: aload_0
方法2:00 09 00 0B 00 0C 00 01
---00 09:access_flags=0x0008|0x0001=ACC_STATIC|ACC_PUBLIC
---00 0B:name_index=#11------>#11 = Utf8 ??????????????main
---00 0C:descriptor_index=#12----->#12 = Utf8 ??????????????([Ljava/lang/String;)V
---00 01:arrtibutes_count=1,緊接著是attribute_info
方法2的code,非指令部分:
00 09 00 00 00 25 00 02 00 01 00 00 00 09
---00 09:attribute_name_index=#9----->#9 = Utf8 ??????????????Code
---00 00 00 25:attribute_length=37,所以整個(gè)屬性表的長(zhǎng)度為43
---00 02:max_stack=2
---00 01:max_locals=1
---00 00 00 09:code_length=17
方法2的code,指令部分
B2 00 02----->getstatic #2:獲取指定類(lèi)的靜態(tài)域,并且壓入到棧頂,#2表示#2 = Fieldref ??????????#16.#17 ???????// ?java/lang/System.out:Ljava/io/PrintStream;
12 03--->ldc #3,將“test”常量值從常量池中壓入到棧頂
B6 00 04---->invokervirtual ?#4,調(diào)用實(shí)例方法,#4 = Methodref ?????????#19.#20 ???????// ?java/io/PrintStream.println:(Ljava/lang/String;)V,即println方法
B1---->return
方法2的Exception:
00 00 ----->沒(méi)有定義throw
方法2的attribute_count:
00 01 //方法最后有個(gè)屬性
方法2的LineNumberTable:
00 0A 00 00 00 0A 00 02
----00 0A:attribute_name_index=#10------>#10 = Utf8 ??????????????LineNumberTable
----00 00 00 0A:attribute_length=10
----00 02:line_number_table_lenght=2,表示lineNumberTable中有2條記錄
00 00 00 04:Test.java第4行對(duì)應(yīng)指令0 --->getstatic ????#2
00 08 00 05:Test.java第5行對(duì)應(yīng)指令8----->8: return
8.sourceFile屬性
00 01:當(dāng)前class文件包含1個(gè)attribute_info
00 0D 00 00 00 02 00 0E
---00 0D:attribute_name_index=#13---->#13 = Utf8 ??????????????SourceFile
---00 00 00 02:attribute_length=2
---00 0E:sourcefile_index=#14---->#14 = Utf8 ??????????????Test.java
至此,class文件中的內(nèi)容分析完畢!
Java
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。