以前還在美團實習的時候,當時讀《深刻理解Java虛擬機》因爲時間緣由只總結了幾個章節,如今把餘下的幾個章節補充上,發表順序有些混亂,章節主線詳見文章彙總|學習Android的一點一滴。數組
本篇將介紹Class文件結構中的各個組成部分,以及每一個部分的定義、數據結構和使用,有利於進一步瞭解虛擬機執行引擎。
1.概述數據結構
運行在各類不一樣平臺上的虛擬機經過載入和執行同一種平臺無關的字節碼來實現了程序的「一次編寫,處處運行」。可見字節碼是構成平臺無關性的基石。佈局
Java虛擬機不和Java等任何語言綁定,只和存儲字節碼的Class文件這種特定的二進制文件格式關聯,且並不關心Class的來源是何種語言,也體現了Java虛擬機的語言無關性。學習
2.類文件結構this
- Class文件是一組以8位字節爲基礎單位的二進制流,各個數據項目嚴格按照順序緊湊地排列在Class文件之中,中間無任何分隔符,當遇到須要佔用8位字節以上空間的數據項時,會按照高位在前的方式分割成若干個8位字節進行存儲。
- Class文件格式採用一種相似於C語言結構體的僞結構來存儲數據,包含兩種數據類型:
- 無符號數:屬於基本數據類型;以u一、u二、u四、u8來分別表明1個字節、2個字節、4個字節和8個字節的無符號數;可用於描述數字、索引引用、數量值或按照UTF-8 編碼構成的字符串值。
- 表:由多個無符號數或其餘表做爲數據項構成的複合數據類型;常以「_info」結尾;可用於描述有層次關係的複合結構的數據。
- 整個Class文件本質上就是一張表,所包含的數據項如圖:
接下來依次介紹表中各個數據項的具體含義。編碼
a. 魔數翻譯
- 魔數(Magic Number):每一個Class文件的頭4個字節
- 做用:判斷該文件是否爲一個能被虛擬機接受的Class文件
b.版本號3d
- 版本號:包含主版本號和一系列次版本號
- 次版本號(Minor Version):第5和第6個字節
- 主版本號(Major Version):第7和第8個字節
- 做用:判斷該文件是否在虛擬機處理的有效範圍內
c.常量池code
- 常量池:使用一個前置的容量計數器(constant_pool_count)加上若干個連續的常量項(constant_pool)來描述
- 容量計數器:從1開始,目的是知足後面某些指向常量池的索引值的數據在特定狀況下須要表達「不引用任何一個常量池項目」的含義,這時能夠把索引值置爲0來表示
- 常量項:如constant_pool_count=2表示常量池中有1個常量項
- 特色:是Class文件的資源倉庫、是Class文件結構中與其餘項目關聯最多的數據類型、是佔用Class文件空間最大的數據項目之1、是在Class文件中第一個出現的表類型數據項目
- 存放內容:兩大類常量
- 字面量(Literal):指Java語言層面的常量概念,如文本字符串、聲明爲final的常量值等
- 符號引用(Symbolic References):指編譯原理方面的概念,包含類和接口的全限定名(Fully Qualified Name)、字段的名稱和描述符(Descriptor)、方法的名稱和描述符
Java代碼進行Javac編譯的過程同虛擬機加載Class文件的過程是動態鏈接的,所以在Class文件中不會保存各個方法、字段的最終內存佈局信息,這就須要虛擬機在運行時從常量池得到對應的符號引用,再在類建立時或運行時解析、翻譯到具體的內存地址之中。cdn
d.訪問標誌
- 訪問標誌(access_flags):常量池結束後兩個字節
- 做用:識別一些類或者接口層次的訪問信息,包括該Class是類仍是接口、是否認義爲public類型、是否認義爲abstract類型、如果類是否被聲明爲final等。具體的標誌位以及含義見圖:
e.類索引、父類索引與接口索引集合
- 類索引(this_class)和父類索引(super_class)都是一個u2類型的數據、接口索引集合(interfaces)是一組u2類型的數據的集合
- 做用: 經過這三項數據來肯定這個類的繼承關係,具體的
- 類索引:肯定這個類的全限定名
- 父類索引:肯定這個類的父類的全限定名
- 接口索引集合:描述這個類所實現的接口,並按照implements語句後的接口順序從左到右排列在接口索引集合中
- 接口索引集合的入口第一項u2類型數據爲接口計數器(interfaces_count),從0計數,如nterfaces_count=2表示該類實現了兩個接口
類全限定名:把類全名中的「.」都替換成「/」,爲了使連續的多個全限定名之間不產生混淆,在使用時最後通常會加入一個「;」表示全限定名結束
f.字段表集合
- 字段表(field_info):用於描述接口或者類中聲明的變量
- 格式如圖
-
- access_flags:存放字段的修飾符,具體的標誌位以及含義見圖:
-
- name_index:存放字段的簡單名稱,即沒有類型和參數修飾的字段名稱
- descriptor_index:存放字段和方法的描述符,包括字段的數據類型、方法的參數列表(包括數量、類型以及順序)和返回值,具體的標誌位以及含義見圖:
-
- attribute_info:屬性表見後
g.方法表集合
- 方法表(methods_info):用於描述接口或者類中聲明的方法
- 格式如圖,可見和描述字段的方式很是相似,僅在訪問標誌和屬性表集合的可選項中有所區別。
h.屬性表集合
- 屬性表(attribute_info):用於描述某些場景專有的信息,在字段表、方法表等都攜帶本身的屬性表集合
- 種類:
- 結構:屬性名須要從常量池中引用一個CONSTANT_Utf8_info類型的常量來表示,屬性值是自定義的、須要經過一個u4的長度屬性說明屬性值所佔用的位數
舉例:class文件結構解析、class文件屬性表解析
3.字節碼指令
- 構成:由一個字節長度的表示某種特定操做含義的操做(操做碼、Opcode)和零至多個表明此操做所需的參數(操做數、Operands)構成
- 特色:非徹底獨立,即並不是每種數據類型和每一種操做都有對應的指令,有些單獨的指令能夠在必要的時候用來將一些不支持的類型轉換爲可被支持的類型
- 分類:將字節碼操做按用途大體分爲9類
- 加載和存儲指令:用於將數據在棧幀中的局部變量表和操做數棧之間來回傳輸
- 運算指令:用於對兩個操做數棧上的值進行某種特定運算,並把結果從新存入到操做棧頂
- 類型轉換指令 :用於實現用戶代碼中的顯式類型轉換操做,或者用於處理字節碼指令集中數據類型相關指令沒法與數據類型一一對應的問題
- 對象建立與訪問指令:用於對象建立,並經過對象訪問指令獲取對象實例或者數組實例中的字段或者數組元素
- 操做數棧管理指令:用於直接操做操做數棧
- 控制轉移指令:用於從指定的位置有條件或無條件地進行指令
- 方法調用和返回指令:用於方法的調用,並根據返回值的類型去返回
- 異常處理指令:用於檢測到異常情況時自動拋出異常
- 同步指令:用於方法內部一段指令序列的同步
具體指令見Java虛擬機字節碼指令簡介