[JVM]-----第六章 類文件結(jié)構(gòu)

1. 概述

? 計算機只能識別01代碼,而不同系統(tǒng)對01代碼的識別也不一樣.運行在虛擬機上成為了一種重要的跨平臺手段.

2. 無關(guān)性的基石

? 構(gòu)成平臺無關(guān)性的基石就是不同虛擬機和所有平臺都統(tǒng)一使用的程序存儲格式--字節(jié)碼(ByteCode).

? 實現(xiàn)語言無關(guān)性的基礎(chǔ)仍然是虛擬機和字節(jié)碼存儲格式,Java虛擬機只與Class文件這種特定的二進制文件格式所關(guān)聯(lián).

3. Class文件的結(jié)構(gòu)

? 這里以JDK1.4時代的Java虛擬機中的定義為主線.

? Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進制流,各個數(shù)據(jù)項目嚴(yán)格按照順序緊湊的排列在一起,中間沒有任何分隔符.當(dāng)遇到需要占用8字節(jié)以上空間的數(shù)據(jù)項時,會按照高位在前的方式分割成若干8位字節(jié)進行存儲.

? Java虛擬機,Class文件格式采用一種類似與C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲數(shù)據(jù),這種偽結(jié)構(gòu)只有兩種數(shù)據(jù)類型:<u>無符號數(shù)</u><u>表</u>.

? <u>無符號數(shù)屬于基本的數(shù)據(jù)類型,以u1,u2,u4,u8來代表1個字節(jié),2個字節(jié),4個字節(jié)和8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字,索引引用,數(shù)量質(zhì)或者UTF-8編碼的字符串值.</u>

? <u>表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構(gòu)成的復(fù)合型數(shù)據(jù)類型.所有表都習(xí)慣性的以"_info"結(jié)尾.</u>

? 無論是無符號數(shù)還是表,當(dāng)需要描述同一類型但數(shù)量不定的多個數(shù)據(jù)時,常使用一個前置的容量計數(shù)器和若干個連續(xù)的數(shù)據(jù)項的形式,這是這連續(xù)若干個數(shù)據(jù)項就被稱為某一類型的集合

(1). 魔數(shù)與Class文件的版本

? 每個Class文件的<u>頭4個字節(jié)成為魔數(shù)</u>,他的唯一作用就是確定這個文件是否是一個能被虛擬機接受的Class文件.

? Class文件的魔值為0xCAFEBABE(咖啡寶貝....,可能后來logo也跟這個有關(guān)系).

? <u>緊接著的4個字節(jié)存儲的是Class文件的版本號</u>:第5和第6字節(jié)是次版本號,第7和第8字節(jié)是主版本號.

(2). 常量池

? 緊接著主次版本號之后的是常量池入口,常量池可以理解為<u>Class文件之中的資源倉庫</u>.,它是Class文件結(jié)構(gòu)中與其他項目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是<u>占用Class文件空間最大的數(shù)據(jù)項目之一</u>.

? 常量池的入口放置一項<u>u2類型</u>的數(shù)據(jù)代表<u>常量池容量計數(shù)值</u>.這個值從1開始計數(shù).索引0表示不引用任何常量池項目.

? 常量池中重要放置兩大類常量:<u>字面量</u><u>符號引用</u>.

(3). 訪問標(biāo)志

? 常量池結(jié)束后,緊接著的兩個字節(jié)代表訪問標(biāo)志,用于表示一些類或者接口層次的訪問信息,包括:這個Class是類還是接口,是否為public,是否為abstract類型,是否為final等等.

(4). 類索引,父類索引與借口索引集合

? 類索引和父類索引都是一個u2類型的數(shù)據(jù),而接口索引集合是一組u2類型數(shù)據(jù)的集合.

? 類索引用于確定類的全限定類名,父類索引用于確定這個類的父類的全限定類名.接口索引集合用來描述這個類實現(xiàn)了那些接口.

? 索引指的是常量池中類符號引用的位置,再從類符號引用找到類的全限定類名.

(5). 字段表集合

? 字段表用于描述接口或者類中聲明的變量 (不包括方法中的局部變量).

每一個字段表的結(jié)構(gòu):

類型 名稱 數(shù)量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attribute_count 1
attribute_info attributes attributes_count

? access_flags:字段修飾符,修飾這個字段的訪問信息,功能與類的訪問標(biāo)志類似.

? name_index:字段的簡單名稱,就是字段名(方法名的話去掉括號)

? descriptor_index:字段和方法的描述符,描述字段的數(shù)據(jù)類型,方法的參數(shù)列表和返回值.

(6). 方法表集合

? 與字段表的結(jié)構(gòu)相同.描述了方法的定義.

? 方法的內(nèi)容結(jié)果編譯器編譯成字節(jié)碼指令后,存放在方法屬性表集合中一個名為"Code"的屬性中.

(7). 屬性表集合

? 屬性表就是之前字段表和方法表結(jié)構(gòu)中的attributes字段,Class文件,字段表,方法表都可以攜帶字節(jié)的屬性表集合,用于描述某些場景專有的信息.

? 這部分的數(shù)據(jù)項目要求不嚴(yán)格,但虛擬機只能識別自己認識的屬性.

1). Code屬性

? Java程序方法體中的代碼經(jīng)過Javac編譯器處理,最終變成字節(jié)碼指令存儲在Code屬性內(nèi).

Code屬性表的結(jié)構(gòu):

類型 名稱 數(shù)量
u2 attribute_name_index 1
u4 attribute_length 1
u2 max_stack 1
u2 max_locals 1
u4 code_length 1
u1 code code_length
u2 exception_table_length 1
exception_info exception_table exception_table_length
u2 attributes_count 1
attribute_info attributes attribute_count

? attribute_name_index:指向常量池中的索引,常量值固定為"Code".

? attribute_length:屬性值的長度,固定為整個屬性表的長度減去6字節(jié)(attribute_name_index和attribute_length的u2+u4).

? max_stack:操作數(shù)棧深度的最大值.

? max_locals:代表局部變量所需的存儲空間.單位是Slot,相當(dāng)于32位.

? code_length和code:用于存儲編譯后生成的字節(jié)碼指令.code_length代表字節(jié)碼長度,code用于存儲字節(jié)碼指令的一系列字節(jié)流.每個指令都是u1. code_length不能超過65535,也就是說方法的內(nèi)容太長的話Javac編譯器會拒絕編譯.

? 如果把Java程序中的信息分為代碼(Java代碼)和<u>元數(shù)據(jù)(Metadata,包括類,字段,方法定義及其他信息)</u>.那么在整個Class文件中,Code屬性用于描述代碼,其他所有數(shù)據(jù)項目都用于描述元數(shù)據(jù).

2). Exceptions屬性

? Exceptions屬性的作用是列舉出方法中可能拋出的受查異常,也就是throws關(guān)鍵字后面列出的異常.

3). LineNumberTable屬性

? 用于描述Java源代碼行號與字節(jié)碼行號(偏移量)之間的對應(yīng)關(guān)系.

4). LocalVariableTable屬性

? 用于描述幀棧中局部變量表中的變量與Java源碼中定義的變量之間的關(guān)系.

5). SourceFile屬性

? 用于記錄生成這個Class文件的源代碼文件名稱.

6). ConstantValue屬性

? 作用是通知虛擬機自動為靜態(tài)變量賦值.

7). InnerClasses屬性

? 用于記錄內(nèi)部類與宿主類之間的關(guān)聯(lián).

8). Deprecated和Synthetic屬性

? Deprecated屬性用于表示某個類,字段或者方法,已經(jīng)被程序作者定位不在推薦使用.可以使用@deprecated注解進行設(shè)置.

? Synthetic屬性代表此字段或者方法不是Java源碼直接產(chǎn)生的,而是由編譯器自動添加的.

9). StackMapTable屬性

? 目的在于替代以前比較消耗性能的基于數(shù)據(jù)流分析的類型推導(dǎo)驗證器.

10). Signature屬性

? 任何類,借口,初始化方法或成員的泛型簽名如果包含了類型變量或參數(shù)化類型,Signature屬性會為它記錄泛型簽名信息.

11).BootstrapMethods屬性

? 用于保存invokedynamic指令引用的引導(dǎo)方法限定符.

4. 字節(jié)碼指令簡介

? 虛擬機的指令由一個字節(jié)長度的,代表某種特定操作含義的數(shù)字,以及跟隨其后的零個或者多個參數(shù)而構(gòu)成.

? Java虛擬機采用面向操作數(shù)棧而不是寄存器的架構(gòu),所以大多數(shù)指令都不包含操作數(shù),只有一個操作碼.

(1). 字節(jié)碼與數(shù)據(jù)類型

? Java虛擬機的指令集中,大多的指令都包含其操作所對應(yīng)的數(shù)據(jù)類型信息(同一個功能對于不同數(shù)據(jù)類型的指令都是不同的).

? 字節(jié)操作碼按用途大致分為9類.

(2). 加載和儲存指令

? 用于將數(shù)據(jù)在幀棧中的局部變量表和操作數(shù)棧之間來回傳輸.包括以下內(nèi)容:

  • 將一個局部變量加載到操作棧:xload,xload_<n>(x是數(shù)據(jù)類型)
  • 將一個數(shù)值從操作數(shù)棧存儲在局部變量表:xstore,xstore_<n>
  • 將一個常量加載到操作數(shù)棧
  • 擴充局部變量表的訪問索引的指令:wide

(3). 運算指令

? 運算或算數(shù)指令用于堆兩個操作數(shù)棧上的值進行某種特定運算,并把結(jié)果存入操作棧頂.

? 所有的算數(shù)指令如下:加法指令,減法指令,乘法指令,除法指令,求余指令,取反指令,位移指令,按位與指令,按位或指令,按位異或指令,局部變量自增指令,比較指令

(4). 類型轉(zhuǎn)換指令

? 可以將兩種不同的數(shù)值類型進行相互轉(zhuǎn)換,一般用于用戶代碼中顯式的類型轉(zhuǎn)換操作.

Java虛擬機直接支持以下數(shù)值類型的寬化類型轉(zhuǎn)換:

  • int類型到long,float,double類型
  • long類型到float,double類型
  • float類型到double類型

相對的,窄化類型轉(zhuǎn)換就必須顯式的使用轉(zhuǎn)換指令來完成,可能會導(dǎo)致不同正負號,數(shù)量級的情況,很可能會丟失數(shù)值的精度.講一個浮點值窄化為整數(shù)類型T(int或者long)時,將遵循以下轉(zhuǎn)換規(guī)則:

  • 如果浮點值是NaN,轉(zhuǎn)換結(jié)果就是0
  • 如果浮點值不是無窮大,那么使用IEEE 754的向零舍入模式取證,得到整數(shù)v,如果v在轉(zhuǎn)換目標(biāo)類型的范圍內(nèi),轉(zhuǎn)換結(jié)果就是v
  • 否則,根據(jù)v的符號,轉(zhuǎn)換為T佐能表示的最大或最小正數(shù).

(5). 對象創(chuàng)建與訪問指令

  • 創(chuàng)建類的實例的指令
  • 創(chuàng)建數(shù)組的指令
  • 訪問類字段(static)和實例字段的指令
  • 把一個數(shù)組元素加載到操作數(shù)棧的指令
  • 將一個操作數(shù)棧的值存儲到數(shù)組元素中的指令
  • 取數(shù)組長度的指令
  • 檢查類實例類型的指令

(6). 操作數(shù)棧管理指令

  • 將操作數(shù)棧棧頂?shù)囊粋€或者兩個元素出棧
  • 復(fù)制棧頂?shù)囊粋€或者兩個數(shù)值并將復(fù)制值或者兩個復(fù)制值壓入棧頂
  • 將棧最頂端的兩個數(shù)值互換

(7). 控制轉(zhuǎn)移指令

? 可以讓Java虛擬機有條件或者無條件地從指定的位置指令而不是控制轉(zhuǎn)移指令的下一條繼續(xù)執(zhí)行(goto)

控制轉(zhuǎn)移指令如下:

  • 條件分支
  • 符合條件分支
  • 無條件分支

(8). 方法調(diào)用和返回指令

  • invokevirtual指令用于調(diào)用對象的實例方法
  • invokeinterface指令用于調(diào)用接口方法
  • invokespecial指令用于調(diào)用一些需要特殊處理的實例方法,包括實例初始化方法,私有方法和父類方法.
  • invokestatic指令用于調(diào)用static方法
  • invokedynameic指令用于調(diào)用運行時動態(tài)解析出調(diào)用點限定符所引用的方法.

(9). 異常處理指令

? Java程序中顯式的拋出異常的操作(throw語句)都由athrow指令來實現(xiàn),許多運行時異常則會在其他Java虛擬機指令檢測到異常狀況時自動拋出.

(10). 同步指令

? Java虛擬機支持方法級的同步和方法內(nèi)部一段指令序列的同步,對應(yīng)著Java代碼中的同步方法和同步代碼塊.這兩種同步都是通過Monitor(管程)支持的.

? 方法級的同步是隱式的,虛擬機通過方法表中的ACC_SYNCHRONIZED訪問標(biāo)志得到方法時候聲明為同步方法.如果為同步方法,執(zhí)行線程就會要求先成功持有管程,然后才能執(zhí)行方法,方法執(zhí)行完畢之后釋放管程.(管程就像線程鎖一樣,限制線程不能進行輪換)

5. 公有設(shè)計和私有實現(xiàn)

? 在Java虛擬機中,公有設(shè)計指的是Java虛擬機規(guī)范:Java虛擬機應(yīng)有的共同程序存儲格式(Class文件格式以及字節(jié)碼指令集).

? 而私有實現(xiàn)就是指虛擬機實現(xiàn)者可以通過在滿足虛擬機規(guī)范的約束下對具體實現(xiàn)做出修改和優(yōu)化,這種伸縮性可以使Java虛擬機獲得更高性能,更低內(nèi)存消耗或者更好的可移植性.

6. Class文件結(jié)構(gòu)的發(fā)展

......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容