一个Java程序是怎样运行起来的【class解析全过程】
首先編寫一測試程序
public class Test {public static void main(String[] args){System.out.println("HelloWorld");}}執(zhí)行javac Test.java 得到Test.class文件(編譯過程有點復雜,這里先不看)
執(zhí)行java Test,控制臺輸出"test",想要弄清楚java程序是怎么運行起來首先得了解清楚class文件
看下Test.class里究竟是什么東西,class文件的內容如下:
上圖中都是以16進制表示,接下來挨個分析其中的內容表示什么意思。class文件中存儲的數(shù)據(jù)可以參考下表:
1、magic 魔數(shù)
CA FE BA BE
魔數(shù),確定該文件是否是虛擬機可以接受的文件
2、class文件版本信息
00 00 00 33
class文件的版本號,51表示jdk1.7.0
3、常量池
3.1常量池入口
00 1D
常量池數(shù)量為29-1=28,每個類只有一個常量池
常量池中放了字符串,常量值,類名稱,字段名,方法名等,反編譯下Test.class,看看常量池中存放了哪些東西
Constant pool:#1 = Methodref #6.#15 // java/lang/Object."<init>":()V#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 <init>#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 // "<init>":()V#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常量池中的項目類型有:
CONSTANT_Utf8_info ?????tag標志位為1, ??UTF-8編碼的字符串,比如類或接口的全限定名,參數(shù)名等
CONSTANT_Integer_info ?tag標志位為3, int整型字面量
CONSTANT_Float_info ????tag標志位為4, float浮點型字面量
CONSTANT_Long_info ????tag標志位為5, long長整形字面量
CONSTANT_Double_info ?tag標志位為6, double雙精度字面量
CONSTANT_Class_info ???tag標志位為7, 類或接口的符號引用,指向包含字符串字面值的CONSTANT_Utf8表
CONSTANT_String_info ???tag標志位為8,字符串類型的字面量,指向包含字符串字面值的CONSTANT_Utf8表
CONSTANT_Fieldref_info ?tag標志位為9, ?字段的符號引用,指向包含該字段所屬類名的CONSTANT_Utf8表
CONSTANT_Methodref_info ?tag標志位為10,類中方法的符號引用,指向包含該方法所屬類型的CONSTANT_Utf8表
CONSTANT_InterfaceMethodref_info tag標志位為11, 接口中方法的符號引用
CONSTANT_NameAndType_info ?tag 標志位為12,字段和方法的名稱以及類型的符號引用
3.2常量池內容
接上,繼續(xù)分析class中的內容,參照 jvm官方文檔 ,看下常量池中究竟是什么東西
常量池1-----0A 00 06 00 0F ??//
??1,0A---tag為10,表示第一個常量類型為CONSTANT_Methodref,參照官方文檔,CONSTANT_Methodref的結構為
CONSTANT_Methodref_info {u1 tag;u2 class_index;u2 name_and_type_index; }所以后面跟了4個字節(jié)
??2,00 06---聲明當前方法類描述符索引值為6 ????// ?java/lang/Object
??3,00 0F---當前方法的名稱和類型索引值為15 ?// ?"<init>":()V
所以,結合上文中反編譯出的內容來看,這幾個16進制翻譯過來正好是
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V常量池2----09 00 10 00 11
??1,09---tag為9,類型為CONSTANT_Fieldref
??2,00 10---聲明當前方法類描述符索引值為16 // java/lang/System
??3,00 11---字段描述符的名稱和類型索引值為17 // ?out:Ljava/io/PrintStream;
這幾個16進制翻譯過來正好是
#2 = Fieldref ? ? ? ? ? #16.#17 ? ? ? ?// ?java/lang/System.out:Ljava/io/PrintStream;
常量池3---08 00 12
???1,08---tag為8,類型為CONSTANT_String,根據(jù)官方文檔,其結構為
CONSTANT_String_info {u1 tag;u2 string_index; }所以后面跟了兩個字節(jié)
?
? ?2,00 12---聲明當前String值所在的索引值為18
當前16進制翻譯過來,表示
#3 = String #18 // test常量池4---0A ?00 13 00 14,對照著上面的分析,
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V常量池5---07 00 15
? ?1,07---tag為7,類型為CONSTANT_Class,根據(jù)官方文檔,其結構為
CONSTANT_Class_info {u1 tag;u2 name_index; }? ?2,00 15----當前類名稱的索引值為21
#5 = Class #21 // Test常量池6---07 00 16,對照上面的分析
#6 = Class #22 // java/lang/Object常量池7---01 00 06 3C 69 6E 69 74 3E
? 1,01---tag為1,類型為CONSTANT_Utf8,根據(jù)官方文檔
CONSTANT_Utf8_info {u1 tag;u2 length;u1 bytes[length]; } ? 2,06---表示字符串的長度為6?
? 3,3C 69 6E 69 74 3E ---字符串<init>
#7 = Utf8 <init>常量池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,類型為CONSTANT_NameAndType,參照jvm官方文檔,其結構為
CONSTANT_NameAndType_info {u1 tag;u2 name_index;u2 descriptor_index; }? 2,00 07---該字段或方法名稱常量索引值為7,即
#7 = Utf8 <init>?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到此常量池結束
4、訪問標志access_flags
00 21----Test類的訪問標志,參照官方文檔,訪問標志有
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之后這個編譯出來的為真
5,類索引,父類索引,接口索引
接下來就是類索引,父類索引,接口索引
00 05------類索引值為#5?
#5 = Class #21 // Test00 06-----父類索引值為#6
#6 = Class #22 // java/lang/Object00 00----類沒有實現(xiàn)接口,接口數(shù)為0,所以后面沒有接口信息
6、字段
00 00----當前類有0個字段
7、方法,指令
00 02----當前類有兩個方法,參照官方文檔,方法的結構如下:
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,方法的訪問標志如下表:
?---00 07:name_index=#7----->#7 = Utf8 ??????????????<init>,可以看出該方法為構造函數(shù)
???---00 08:descriptor_index=#8------>#8 = Utf8 ??????????????()V
???---00 01:attributes_count=1,所以緊隨其后就是attribute_info部分,根據(jù)官方文檔,其結構如下:
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,所以整個屬性表的長度為29+6=35,見官方文檔說明: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,調用超類構造方法
?????---B1--->return
方法1的Exception:
00 00:方法沒有throw異常
方法1的attribute count:
00 01://方法1最后有一個屬性塊,其結構如下:
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,表示這個LineNumberTable中有一條記錄
?????---00 00 00 01:表示Test.java的第一行代碼對應指令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,所以整個屬性表的長度為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:獲取指定類的靜態(tài)域,并且壓入到棧頂,#2表示#2 = Fieldref ??????????#16.#17 ???????// ?java/lang/System.out:Ljava/io/PrintStream;
12 03--->ldc #3,將“test”常量值從常量池中壓入到棧頂
B6 00 04---->invokervirtual ?#4,調用實例方法,#4 = Methodref ?????????#19.#20 ???????// ?java/io/PrintStream.println:(Ljava/lang/String;)V,即println方法
B1---->return
方法2的Exception:
00 00 ----->沒有定義throw
方法2的attribute_count:
00 01 //方法最后有個屬性
方法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行對應指令0 --->getstatic ????#2
00 08 00 05:Test.java第5行對應指令8----->8: return
8.sourceFile屬性
00 01:當前class文件包含1個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文件中的內容分析完畢!
總結
以上是生活随笔為你收集整理的一个Java程序是怎样运行起来的【class解析全过程】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年互联网企业软件测试面试题(超实
- 下一篇: java定时执行一段代码