類文件結構

類文件結構

JVM 的「無關性」

談論 JVM 的無關性,主要有如下兩個:
java

  • 平臺無關性:任何操做系統都能運行 Java 代碼
  • 語言無關性: JVM 能運行除 Java 之外的其餘代碼

Java 源代碼首先須要使用 Javac 編譯器編譯成 .class 文件,而後由 JVM 執行 .class 文件,從而程序開始運行。安全

JVM 只認識 .class 文件,它不關心是何種語言生成了 .class 文件,只要 .class 文件符合 JVM 的規範就能運行。
目前已經有 JRuby、Jython、Scala 等語言可以在 JVM 上運行。它們有各自的語法規則,不過它們的編譯器
都能將各自的源碼編譯成符合 JVM 規範的 .class 文件,從而可以藉助 JVM 運行它們。編碼

Java 語言中的各類變量、關鍵字和運算符號的語義最終都是由多條字節碼命令組合而成的,
所以字節碼命令所能提供的語義描述能力確定會比 Java 語言自己更增強大。
所以,有一些 Java 語言自己沒法有效支持的語言特性,不表明字節碼自己沒法有效支持。

Class 文件結構

Class 文件時二進制文件,它的內容具備嚴格的規範,文件中沒有任何空格,全都是連續的 0/1。Class 文件
中的全部內容被分爲兩種類型:無符號數、表。操作系統

  • 無符號數

無符號數表示 Class 文件中的值,這些值沒有任何類型,但有不一樣的長度。u一、u二、u四、u8 分別表明 1/2/4/8 字節的無符號數。對象


由多個無符號數或者其餘表做爲數據項構成的符合數據類型。繼承

Class 文件具體由如下幾個構成:索引

  • 魔數
  • 版本信息
  • 常量池
  • 訪問標誌
  • 類索引、父類索引、接口索引集合
  • 字段表集合
  • 方法表集合
  • 屬性表集合

魔數

Class 文件的頭 4 個字節稱爲魔數,用來表示這個 Class 文件的類型。接口

Class 文件的魔數是用 16 進製表示的「CAFE BABE」,是否是很具備浪漫色彩?ip

魔數至關於文件後綴名,只不事後綴名容易被修改,不安全,所以在 Class 文件中標識文件類型比較合適。

版本信息

緊接着魔數的 4 個字節是版本信息,5-6 字節表示次版本號,7-8 字節表示主版本號,它們表示當前 Class 文件中使用的是哪一個版本的 JDK。字符串

高版本的 JDK 能向下兼容之前版本的 Class 文件,但不能運行之後版本的 Class 文件,即時文件格式並未發生任何變化,虛擬機也必需拒絕執行超過其版本號的 Class 文件。

常量池

版本信息以後就是常量池,常量池中存放兩種類型的常量:

  • 字面值常量

字面值常量就是咱們在程序中定義的字符串、被 final 修飾的值。

  • 符號引用

符號引用就是咱們定義的各類名字:類和接口的全限定名、字段的名字和描述符、方法的名字和描述符。

常量池的特色

  • 常量池中常量數量不固定,所以常量池開頭放置一個 u2 類型的無符號數,用來存儲當前常量池的容量。
  • 常量池的每一項常量都是一個表,表開始的第一位是一個 u1 類型的標誌位(tag),表明當前這個常量屬於哪一種常量類型。

常量池中常量類型

類型 tag 描述 
CONSTANT_utf8_info 1 UTF-8編碼的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮點型字面量
CONSTANT_Long_info 5 長整型字面量
CONSTANT_Double_info 6 雙精度浮點型字面量
CONSTANT_Class_info 7 類或接口的符號引用
CONSTANT_String_info 8 字符串類型字面量
CONSTANT_Fieldref_info 9 字段的符號引用
CONSTANT_Methodref_info 10 類中方法的符號引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符號引用
CONSTANT_NameAndType_info 12 字段或方法的符號引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 標識方法類型
CONSTANT_InvokeDynamic_info 18 表示一個動態方法調用點

對於 CONSTANT_Class_info(此類型的常量表明一個類或者接口的符號引用),它的二維表結構以下:

類型 名稱 數量
u1 tag 1
u2 name_index 1

tag 是標誌位,用於區分常量類型;name_index 是一個索引值,它指向常量池中一個 CONSTANT_Utf8_info 類型常量,此常量表明這個類(或接口)的全限定名,這裏 name_index 值若爲 0x0002,也便是指向了常量池中的第二項常量。

CONSTANT_Utf8_info 型常量的結構以下:

類型 名稱 數量
u1 tag 1
u2 length 1
u1 bytes length

tag 是當前常量的類型;length 表示這個字符串的長度;bytes 是這個字符串的內容(採用縮略的 UTF8 編碼)

訪問標誌

在常量池結束以後,緊接着的兩個字節表明訪問標誌,這個標誌用於識別一些類或者接口層次的訪問信息,包括:這個 Class 是類仍是接口;是否認義爲 public 類型;是否被 abstract/final 修飾。

類索引、父類索引、接口索引集合

類索引和父類索引都是一個 u2 類型的數據,而接口索引集合是一組 u2 類型的數據的集合,Class 文件中由這三項數據來肯定類的繼承關係。類索引用於肯定這個類的全限定名,父類索引用於肯定這個類的父類的全限定名。

因爲 Java 不容許多重繼承,因此父類索引只有一個,除了 java.lang.Object 以外,全部的 Java 類都有父類,所以除了 java.lang.Object 外,全部 Java 類的父類索引都不爲 0。一個類可能實現了多個接口,所以用接口索引集合來描述。這個集合第一項爲 u2 類型的數據,表示索引表的容量,接下來就是接口的名字索引。

類索引和父類索引用兩個 u2 類型的索引值表示,它們各自指向一個類型爲 CONSTANT_Class_info 的類描述符常量,經過該常量總的索引值能夠找到定義在 CONSTANT_Utf8_info 類型的常量中的全限定名字符串。

字段表集合

字段表集合存儲本類涉及到的成員變量,包括實例變量和類變量,但不包括方法中的局部變量。

每個字段表只表示一個成員變量,本類中的全部成員變量構成了字段表集合。字段表結構以下:

類型 名稱 數量 說明
u2 access_flags 1 字段的訪問標誌,與類稍有不一樣
u2 name_index 1 字段名字的索引
u2 descriptor_index 1 描述符,用於描述字段的數據類型。
基本數據類型用大寫字母表示;
對象類型用「L 對象類型的全限定名」表示。
u2 attributes_count 1 屬性表集合的長度
u2 attributes attributes_count 屬性表集合,用於存放屬性的額外信息,如屬性的值。
字段表集合中不會出現從父類(或接口)中繼承而來的字段,但有可能出現本來 Java 代碼中不存在的字段,譬如在內部類中爲了保持對外部類的訪問性,會自動添加指向外部類實例的字段。

方法表集合

方法表結構與屬性表相似。

volatile 關鍵字 和 transient 關鍵字不能修飾方法,因此方法表的訪問標誌中沒有 ACC_VOLATILE 和 ACC_TRANSIENT 標誌。

方法表的屬性表集合中有一張 Code 屬性表,用於存儲當前方法經編譯器編譯後的字節碼指令。

屬性表集合

每一個屬性對應一張屬性表,屬性表的結構以下:

類型 名稱 數量
u2 attribute_name_index 1
u4 attribute_length 1
u1 info attribute_length
相關文章
相關標籤/搜索