《深入理解Java虚拟机》笔记4——类文件结构
代碼編譯的結(jié)果從本地機(jī)器碼轉(zhuǎn)變?yōu)樽止?jié)碼,是存儲(chǔ)格式發(fā)展的一小步,卻是編程語言發(fā)展的一大步。
由于最近十年內(nèi)虛擬機(jī)以及大量建立在虛擬機(jī)之上的程序語言如雨后春筍般出現(xiàn)并蓬勃發(fā)展,將我們編寫的程序編譯成二進(jìn)制本地機(jī)器碼(Native Code)已不再是唯一的選擇,越來越多的程序語言選擇了操作系統(tǒng)和機(jī)器指令集無關(guān)的、平臺(tái)中立的格式作為程序編譯后的存儲(chǔ)格式。
無關(guān)性的基石
- Java剛誕生的宣傳口號(hào):一次編寫,到處運(yùn)行(Write Once, Run Anywhere)。其最終實(shí)現(xiàn)在操作系統(tǒng)的應(yīng)用層:Sun公司以及其他虛擬機(jī)提供商發(fā)布了許多可以運(yùn)行在各種不同平臺(tái)的虛擬機(jī),這些虛擬機(jī)都可以載入和執(zhí)行同一種平臺(tái)無關(guān)的字節(jié)碼。
- 字節(jié)碼(ByteCode)是構(gòu)成平臺(tái)無關(guān)的基石;
- 另外虛擬機(jī)的語言無關(guān)性也越來越被開發(fā)者所重視,JVM設(shè)計(jì)者在最初就考慮過實(shí)現(xiàn)讓其他語言運(yùn)行在Java虛擬機(jī)之上的可能性,如今已發(fā)展出一大批在JVM上運(yùn)行的語言,比如Clojure、Groovy、JRuby、Jython、Scala;
- 實(shí)現(xiàn)語言無關(guān)性的基礎(chǔ)仍是虛擬機(jī)和字節(jié)碼存儲(chǔ)格式,Java虛擬機(jī)不和包括Java在內(nèi)的任何語言綁定,它只與Class文件這種特定的二進(jìn)制文件格式所關(guān)聯(lián),這使得任何語言的都可以使用特定的編譯器將其源碼編譯成Class文件,從而在虛擬機(jī)上運(yùn)行。
?
Java虛擬機(jī)提供的語言無關(guān)性
Class類文件的結(jié)構(gòu)
- Class文件是一組以8個(gè)字節(jié)為基礎(chǔ)單位的二進(jìn)制流(可能是磁盤文件,也可能是類加載器直接生成的),各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊地排列,中間沒有任何分隔符;
- Class文件格式采用一種類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù),其中只有兩種數(shù)據(jù)類型:無符號(hào)數(shù)和表;
- 無符號(hào)數(shù)屬于基本的數(shù)據(jù)類型,以u(píng)1、u2、u4和u8來分別代表1個(gè)字節(jié)、2個(gè)字節(jié)、4個(gè)字節(jié)和8個(gè)字節(jié)的無符號(hào)數(shù),可以用來描述數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串值;
- 表是由多個(gè)無符號(hào)數(shù)獲取其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型,習(xí)慣以“_info”結(jié)尾;
- 無論是無符號(hào)數(shù)還是表,當(dāng)需要描述同一個(gè)類型但數(shù)量不定的多個(gè)數(shù)據(jù)時(shí),經(jīng)常會(huì)使用一個(gè)前置的容量計(jì)數(shù)器加若干個(gè)連續(xù)的數(shù)據(jù)項(xiàng)的形式,這時(shí)稱這一系列連續(xù)的某一類型的數(shù)據(jù)未某一類型的集合。
?
下面我以自己本機(jī)寫的一個(gè)簡單的Java文件來學(xué)習(xí)其中各個(gè)部分的含義:
使用javac編譯成TestClass.class文件,使用16進(jìn)制打開:
?
使用javap命令輸出Class文件信息:
?
魔數(shù)和版本(magic、version)
- Class文件的頭4個(gè)字節(jié),唯一作用是確定文件是否為一個(gè)可被虛擬機(jī)接受的Class文件,固定為“0xCAFEBABE”。
- 第5和第6個(gè)字節(jié)是次版本號(hào),第7和第8個(gè)字節(jié)是主版本號(hào)(0x0034為52,對(duì)應(yīng)JDK版本1.8);能向下兼容之前的版本,無法運(yùn)行后續(xù)的版本;
常量池(constant_pool)
- 常量池可以理解為Class文件之中的資源倉庫,是Class文件結(jié)構(gòu)中與其他項(xiàng)目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是占用Class文件空間最大的數(shù)據(jù)項(xiàng)之一;
- 由于常量池中的常量數(shù)量不固定,因此需要在常量池前放置一項(xiàng)u2類型的數(shù)據(jù)來表示容量,該值是從1開始的,上圖的0x0013為十進(jìn)制的19,代表常量池中有18項(xiàng)常量,索引值范圍為1~18;
- 常量池主要存放兩大類常量:字面量(Literal,比較接近Java的常量概念,比如文本字符串和final常量等)和符號(hào)引用(Symbolic References,主要包括類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符);
- Java代碼在javac編譯時(shí)不會(huì)有“連接”這一步驟,而是在虛擬機(jī)加載Class文件的時(shí)候進(jìn)行動(dòng)態(tài)連接;所以在Class文件不會(huì)保存各個(gè)方法、字段和最終內(nèi)存布局信息;當(dāng)虛擬機(jī)運(yùn)行時(shí)需要從常量池獲取對(duì)應(yīng)的符號(hào)引用,再在類創(chuàng)建時(shí)或運(yùn)行時(shí)解析、翻譯到具體的內(nèi)存地址中;
- JDK 1.7中常量池共有14種不同的表結(jié)構(gòu)數(shù)據(jù),這些表結(jié)構(gòu)開始的第一位是一個(gè)u1類型的標(biāo)志位,代表當(dāng)前常量的類型,具體如下圖所示:
?
- 之所以說常量池是最繁瑣的數(shù)據(jù)就是因?yàn)檫@14種常量類型都有自己的結(jié)結(jié)構(gòu)。可以結(jié)合下圖中各個(gè)表結(jié)構(gòu)的說明和之前使用javap解析的文件內(nèi)容一起看。
?
?
- 第1項(xiàng):0x0A(15標(biāo)志為方法句柄),0x0004(指向第4項(xiàng)的類描述符),0x000F(指向第15項(xiàng)的名稱及類型描述符);
- 第2項(xiàng):0x09(9標(biāo)志為字段符號(hào)引用),0x0003(指向第3項(xiàng)類描述符),0x0010(指向第16項(xiàng)的名稱及類型描述符);
- 第3項(xiàng):0x07(7標(biāo)志為類符號(hào)引用),0x0011(指向第17項(xiàng)全限定名常量項(xiàng));
- 第4項(xiàng):0x07(7標(biāo)志為類符號(hào)引用),0x0012(指向第18項(xiàng)全限定名常量項(xiàng));
- 第5項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0001(字符串占用1個(gè)字節(jié)),6D(字符“m”);
- 第6項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0001(字符串占用1個(gè)字節(jié)),49(字符“I”);
- 第7項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0006(字符串占用6個(gè)字節(jié)),3C 69 6E 69 74 3E(字符“”);
- 第8項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0003(字符串占用3個(gè)字節(jié)),28 29 56(字符“()V”);
- 第9項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0004(字符串占用4個(gè)字節(jié)),43 6F 64 65(字符“Code”);
- 第10項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x000F(字符串占用15個(gè)字節(jié)),4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65(字符“LineNumberTable”);
- 第11項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0003(字符串占用3個(gè)字節(jié)),69 6E 63(字符“inc”);
- 第12項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0003(字符串占用3個(gè)字節(jié)),28 29 49(字符“()I”);
- 第13項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x000A(字符串占用10個(gè)字節(jié)),53 6F 75 72 63 65 46 69 6C 65(字符“SourceFile”);
- 第14項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x000E(字符串占用14個(gè)字節(jié)),54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61(字符“TestClass.java”);
- 第15項(xiàng):0x0C(12標(biāo)志為名稱和類型符號(hào)引用),0x0007(指向第7項(xiàng)名稱常量項(xiàng)), 0x0008(指向第8項(xiàng)描述符常量項(xiàng));
- 第16項(xiàng):0x0C(12標(biāo)志為名稱和類型符號(hào)引用),0x0005(指向第5項(xiàng)名稱常量項(xiàng)), 0x0006(指向第6項(xiàng)描述符常量項(xiàng));
- 第17項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x001F(字符串占用31個(gè)字節(jié)),63 6F 6D 2F 67 69 6E 6F 62 65 66 75 6E 6E 79 2F 63 6C 61 7A 7A 2F 54 65 73 74 43 6C 61 73 73(字符“com/ginobefunny/clazz/TestClas”);
- 第18項(xiàng):0x01(1標(biāo)志為UTF-字符串常量),0x0010(字符串占用16個(gè)字節(jié)),6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74(字符“java/lang/Object”);
訪問標(biāo)志
- 緊接在常量池后面的是兩個(gè)字節(jié)的訪問標(biāo)志,用于標(biāo)識(shí)類或接口的訪問信息;
- 訪問標(biāo)志一個(gè)有16個(gè)標(biāo)志位,但目前只采用了其中8位,本例子中的0x0021標(biāo)識(shí)為一個(gè)public的普通類;
類索引、父類索引與接口索引集合
- 類索引:u2類型的數(shù)據(jù),用于確定類的全限定名。本例子中為0x0003,指向常量池中第3項(xiàng);
- 父類索引:u2類型的數(shù)據(jù),用于確定父類的全限定名。本例子中為0x0004,指向常量池中第4項(xiàng);
- 接口索引計(jì)算器:u2類型的數(shù)據(jù),用于表示索引集合的容量。本例子中為0x0000,說明沒有實(shí)現(xiàn)接口;
- 接口索引集合:一組u2類型的數(shù)據(jù)的集合,用于確定實(shí)現(xiàn)的接口(對(duì)于接口來說就是extend的接口)。本例子不存在。
字段表集合
?
- 用于描述接口或者類中聲明的變量,包括類級(jí)變量和實(shí)例級(jí)變量,但不包括方法內(nèi)部聲明的局部變量;它不會(huì)列出從父類和超類繼承而來的字段;
- 0x0001表示這個(gè)類只有一個(gè)字段表數(shù)據(jù);
- 字段修飾符放在access_flag中,是一個(gè)u2的數(shù)據(jù)類型,0x0002表示為private的屬性;
- 字段名稱name_index,是一個(gè)u2的數(shù)據(jù)類型,0x0005表示該屬性的名稱為常量池的第5項(xiàng);
- 字段描述符descriptor_index,是一個(gè)u2的數(shù)據(jù)類型,0x0006表示該屬性的描述符為常量池的第6項(xiàng),其值“I”表示類型為整形;
- 字段屬性計(jì)算器和屬性集合:0x0000表示該例子中不存在;
方法表集合
- 和字段表集合的方式幾乎一樣;
- 方法里面的代碼經(jīng)過編譯器編譯成字節(jié)碼指令后,存放在方法屬性表集合中一個(gè)名為Code的屬性里面;
- 0x0002表示這個(gè)類有兩個(gè)方法表數(shù)據(jù),分別是編譯器添加的實(shí)例構(gòu)造器和源碼中的方式inc();
- 第一個(gè)方法的訪問標(biāo)志是0x0001(public方法),名稱索引值為0x0007(常量池第7項(xiàng),“”),描述符索引值為0x0008(常量池第8項(xiàng),“()V”),屬性表計(jì)算器為0x0001(有一項(xiàng)屬性),屬性名稱索引為0x0009(常量池第9項(xiàng),“Code”);
- 根據(jù)“6.3.7.1 Code屬性”說明,屬性值的長度為23(0x0000001D表示29,但需要減去屬性名稱索引和屬性長度固定的6個(gè)字節(jié)長度),操作數(shù)棧深度的最大值為1(0x0001,虛擬機(jī)運(yùn)行時(shí)根據(jù)這個(gè)值來分配棧幀中操作棧深度),局部變量表所需要的存儲(chǔ)空間為1個(gè)Slot(0x0001,Slot是內(nèi)存分配的最小單位),字節(jié)碼長度為5(0x00000005),分別為2A(aload_0,將第0個(gè)Slot中為reference類型的本地變量推送到操作數(shù)棧頂)、B7(invokespecial,以棧頂?shù)膔eference類型的數(shù)據(jù)所指向的對(duì)象作為方法接收者,調(diào)用此對(duì)象的實(shí)例構(gòu)造器方法、private方法或者它父類的方法,后面接著一個(gè)u2的參數(shù)指向常量池的方法引用)、0x0001(表示常量池的第1項(xiàng),即Object類的方法)、B1(對(duì)應(yīng)的指令為return,返回值為void);顯式異常表為空(0x0000,計(jì)數(shù)器為0);該Code屬性還內(nèi)嵌1個(gè)屬性(0x0001),屬性的名稱索引為0x000A(即“LineNumberTable”屬性,用于記錄對(duì)應(yīng)的代碼行數(shù)),該內(nèi)嵌屬性的長度為6(0x00000006),對(duì)應(yīng)的行數(shù)信息為源碼的第3行(0x000100000003);
- 第二個(gè)方法的訪問標(biāo)志是0x0001(public方法),名稱索引值為0x000B(常量池第11項(xiàng),“inc”),描述符索引值為0x000C(常量池第12項(xiàng),“()I”),屬性表計(jì)算器為0x0001(有一項(xiàng)屬性),屬性名稱索引為0x0009(常量池第9項(xiàng),“Code”);
- 根據(jù)“6.3.7.1 Code屬性”說明,屬性值的長度為25(0x0000001F表示31,但需要減去屬性名稱索引和屬性長度固定的6個(gè)字節(jié)長度),操作數(shù)棧深度的最大值為2(0x0002),局部變量表所需要的存儲(chǔ)空間為1個(gè)Slot(0x0001),字節(jié)碼長度為7(0x00000007),分別為2A(aload_0)、B4(getfield,后面接著一個(gè)u2的參數(shù)指向常量池的屬性引用)、0x0002(表示常量池的第2項(xiàng),即TestClass類的m屬性)、04(對(duì)應(yīng)的指令為iconst_1)、60(對(duì)應(yīng)的指令為iadd,整形求和)、AC(對(duì)應(yīng)的指令為ireturn,返回值為整形);顯式異常表為空(0x0000,計(jì)數(shù)器為0);該Code屬性還內(nèi)嵌1個(gè)屬性(0x0001),屬性的名稱索引為0x000A(即“LineNumberTable”屬性,用于記錄對(duì)應(yīng)的代碼行數(shù)),該內(nèi)嵌屬性的長度為6(0x00000006),對(duì)應(yīng)的行數(shù)信息為源碼的第8行(0x000100000008);
屬性表集合
- 在Class文件、字段表、方法表都可以攜帶自己的屬性表集合;
- 屬性表集合的限制較為寬松,不再要求嚴(yán)格的順序,只要屬性名不重復(fù)即可;
- 以下是Java虛擬機(jī)規(guī)范里預(yù)定義的虛擬機(jī)實(shí)現(xiàn)應(yīng)當(dāng)能識(shí)別的屬性:
?
?
虛擬機(jī)規(guī)范預(yù)定義的屬性2
- 接著我們的例子的Class文件還有最后一段:0x0001表示該Class有一個(gè)屬性,0x000D表示屬性名索引為第13項(xiàng)(對(duì)應(yīng)“SourceFile”),0x00000002表示該屬性長度為2,0x000E表示該類的SourceFile名稱為第14項(xiàng)(對(duì)應(yīng)“TestClass.java”)。
Code屬性
Java程序方法體中的代碼經(jīng)過javac編譯后,字節(jié)碼指令存放在Code屬性,其屬性表結(jié)構(gòu)如下:
?
Exceptions屬性
方法描述時(shí)throws關(guān)鍵字后面列舉的異常,和Code屬性里的異常表不同。其屬性表結(jié)構(gòu)如下:
?
LineNumberTable屬性
用于描述Java源碼行號(hào)與字節(jié)碼行號(hào)之間的對(duì)應(yīng)關(guān)系,它不是必須的,可以通過javac -g:none取消該信息。沒有該信息的影響是運(yùn)行時(shí)拋異常不會(huì)顯示出錯(cuò)的行號(hào),在代碼調(diào)試時(shí)無法按照源碼行來設(shè)置斷點(diǎn)。
?
LocalVariableTable屬性
用于描述棧幀中局部變量與Java源碼中定義的變量之間的關(guān)系,它不是運(yùn)行時(shí)必須的,可以通過javac -g:none取消該信息。如果沒有這個(gè)屬性,所有的參數(shù)名稱都會(huì)丟失,取之以arg0、arg1這樣的占位符來替代。
?
其中l(wèi)ocal_variable_info項(xiàng)代表了一個(gè)棧幀與源碼中局部變量的關(guān)聯(lián),如下所示:
SourceFile屬性
用于記錄生成這個(gè)Class的源碼文件名稱,這個(gè)屬性也是可選的。
ConstantValue屬性
作用是通知虛擬機(jī)自動(dòng)為靜態(tài)變量賦值,只有被static關(guān)鍵字修飾的變量才可以用這個(gè)屬性。對(duì)于非static類型的變量的賦值是在實(shí)例構(gòu)造器方法中進(jìn)行的;而對(duì)于類變量有兩種方式:在類構(gòu)造器方法中或者使用ConstantValue屬性。目前Sun javac編譯器的選擇是:同時(shí)使用final和static修飾的變量且為基本數(shù)據(jù)類型或String類型使用ConstantValue屬性初始化,否則使用初始化
?
InnerClass屬性
用于記錄內(nèi)部類與宿主類之間的關(guān)聯(lián)。
?
其中number_of_class代表需要記錄多少個(gè)內(nèi)部類信息,每個(gè)內(nèi)部類的信息都由一個(gè)inner_class_info表進(jìn)行描述。
Deprecated及Synthetic屬性
Deprecated(不推薦使用)和Synthetic(不是由Java源碼直接產(chǎn)生編譯器自行添加的,有兩個(gè)例外是實(shí)例構(gòu)造器和類構(gòu)造器)這兩個(gè)屬性都屬于布爾屬性,只存在有和沒有的區(qū)別,沒有屬性值的概念。在屬性結(jié)構(gòu)中attribute_length的數(shù)據(jù)值必須為0x00000000。
StackMapTable屬性
這是一個(gè)復(fù)雜的變長屬性,位于Code屬性的屬性表中。這個(gè)屬性會(huì)在虛擬機(jī)類加載的字節(jié)碼驗(yàn)證階段被新類型檢查驗(yàn)證器使用,目的在于代替以前比較消耗性能的基于數(shù)據(jù)流分析的類型推導(dǎo)驗(yàn)證器。
Signature屬性
一個(gè)可選的定長屬性,在JDK 1.5發(fā)布后增加的,任何類、接口、初始化方法或成員的泛型簽名如果包含了類型變量或參數(shù)化類型,則Signature屬性會(huì)為它記錄泛型簽名信息。這主要是因?yàn)镴ava的泛型采用的是擦除法實(shí)現(xiàn)的偽泛型,在字節(jié)碼中泛型信息編譯之后統(tǒng)統(tǒng)被擦除,在運(yùn)行期無法將泛型類型與用戶定義的普通類型同等對(duì)待。通過Signature屬性,Java的反射API能夠獲取泛型類型。
BootstrapMethods屬性
一個(gè)復(fù)雜的變長屬性,位于類文件的屬性表中,用于保存invokedynamic指令引用的引導(dǎo)方法限定符。
字節(jié)碼指令簡介
Java虛擬機(jī)的指令由一個(gè)字節(jié)長度的、代表著特定操作含義的數(shù)字(操作碼)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)(稱為操作數(shù))而構(gòu)成。由于Java虛擬機(jī)采用面向操作數(shù)棧而不是寄存器的架構(gòu),所以大多數(shù)的指令都不包含操作數(shù),只有一個(gè)操作碼。
在指令集中大多數(shù)的指令都包含了其操作所對(duì)應(yīng)的數(shù)據(jù)類型信息,如iload指令用于從局部變量表中加載int類型的數(shù)據(jù)到操作數(shù)棧中。
- 加載和存儲(chǔ)指令:iload/iload_等(加載局部變量到操作棧)、istore/istore_等(從操作數(shù)棧存儲(chǔ)到局部變量表)、bipush/sipush/ldc/iconst_(加載常量到操作數(shù)棧)、wide(擴(kuò)充局部變量表訪問索引);
- 運(yùn)算指令:沒有直接支持byte、short、char和boolean類型的算術(shù)指令而采用int代替;iadd/isub/imul/idiv加減乘除、irem求余、ineg取反、ishl/ishr位移、ior按位或、iand按位與、ixor按位異或、iinc局部變量自增、dcmpg/dcmpl比較;
- 類型轉(zhuǎn)換指令:i2b/i2c/i2s/l2i/f2i/f2l/d2i/d2l/d2f;
- 對(duì)象創(chuàng)建與訪問指令:new創(chuàng)建類實(shí)例、newarray/anewarray/multianewarray創(chuàng)建數(shù)組、getfield/putfield/getstatic/putstatic訪問類字段或?qū)嵗侄巍aload/iaload/aaload把一個(gè)數(shù)組元素加載到操作數(shù)棧、bastore/iastore/aastore將一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素中、arraylength取數(shù)組長度、instanceof/checkcast檢查類實(shí)例類型;
- 操作數(shù)棧管理指令:pop/pop2一個(gè)或兩個(gè)元素出棧、dup/dup2復(fù)制棧頂一個(gè)或兩個(gè)數(shù)組并將復(fù)制值或雙份復(fù)制值重新壓力棧頂、swap交互棧頂兩個(gè)數(shù)值;
- 控制轉(zhuǎn)移指令:ifeq/iflt/ifnull條件分支、tableswitch/lookupswitch復(fù)合條件分支、goto/jsr/ret無條件分支;
- 方法調(diào)用和返回指令:invokevirtual/invokeinterface/invokespecial/invokestatic/invokedynamic方法調(diào)用、ireturn/lreturn/areturn/return方法返回;
- 異常處理指令:athrow
- 同步指令:monitorenter/monitorexit
公有設(shè)計(jì)和私有實(shí)現(xiàn)
- Java虛擬機(jī)的實(shí)現(xiàn)必須能夠讀取Class文件并精確實(shí)現(xiàn)包含在其中的Java虛擬機(jī)代碼的含義;
- 但一個(gè)優(yōu)秀的虛擬機(jī)實(shí)現(xiàn),通常會(huì)在滿足虛擬機(jī)規(guī)范的約束下具體實(shí)現(xiàn)做出修改和優(yōu)化;
- 虛擬機(jī)實(shí)現(xiàn)的方式主要有兩種:將輸入的Java虛擬機(jī)代碼在加載或執(zhí)行時(shí)翻譯成另外一種虛擬機(jī)的指令集或宿主主機(jī)CPU的本地指令集。
Class文件結(jié)構(gòu)的發(fā)展
- Class文件結(jié)構(gòu)一直比較穩(wěn)定,主要的改進(jìn)集中向訪問標(biāo)志、屬性表這些可擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)中添加內(nèi)容;
- Class文件格式所具備的平臺(tái)中立、緊湊、穩(wěn)定和可擴(kuò)展的特點(diǎn),是Java技術(shù)體系實(shí)現(xiàn)平臺(tái)無關(guān)、語言無關(guān)兩項(xiàng)特性的重要支柱;
本章小結(jié)
本章詳細(xì)講解了Class文件結(jié)構(gòu)的各個(gè)部分,通過一個(gè)實(shí)例演示了Class的數(shù)據(jù)是如何存儲(chǔ)和訪問的,后面的章節(jié)將以動(dòng)態(tài)的、運(yùn)行時(shí)的角度去看看字節(jié)碼在虛擬機(jī)執(zhí)行引擎是怎樣被解析執(zhí)行的。
參考資料
- 《深入理解Java虛擬機(jī)——JVM高級(jí)特性與最佳實(shí)踐》-周志明
?
原文:https://meandni.com/2019/01/14/jvm_note4/
總結(jié)
以上是生活随笔為你收集整理的《深入理解Java虚拟机》笔记4——类文件结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《深入理解Java虚拟机》笔记3——7种
- 下一篇: java美元兑换,(Java实现) 美元