深入理解Java虛擬機器 – 類檔案結構

beyondlicg發表於2019-02-12

程式碼編譯的結果從本地機器碼轉化為位元組碼,是儲存格式發展的一小步,卻是程式語言發展得一大步。

概述

本文沒有深入講解類檔案結構,而是作為類載入機制的前門知識,因為類載入機制需要對類檔案有一定的瞭解基礎,如果你希望從本文中獲取詳細的類檔案結構資訊,可能本文不是很適合你。

無關性的基石

Java虛擬機器不和包括Java在內的任何語言繫結,它只與“Class”這種特定的二進位制檔案格式所關聯,Class檔案包含了Java虛擬機器指令集和符號表以及若干輔助資訊。

深入理解Java虛擬機器 – 類檔案結構

Class類檔案的結構

任何一個Class檔案都對應著唯一一個類或介面的定義資訊。Class檔案是一組以8位位元組為單位的二進位制流,各個資料專案嚴格按照順序緊湊排列在Class檔案內,中間沒有任何分隔符,所以Class檔案儲存的幾乎是程式執行時的必要資料。
Class檔案採用類似於C語言結構體的偽結構來儲存資料,偽結構體只有兩種資料型別:無符號數和表。

  • 無符號數
    無符號數屬於基本的資料型別,u1、u2、u4、u8分別代表1個位元組、2個位元組、4個位元組、8個位元組的無符號數,無符號數可以用來描述數字、索引引用、數量值等

  • 表是由多個無符號數或其他表組成的複合資料型別,所有表都以_info結尾,表用於描述有層次關係的複合結構的資料。
    深入理解Java虛擬機器 – 類檔案結構

魔數與Class檔案版本

Class檔案的頭4個位元組稱為魔數,唯一作用是確定這個Class檔案是否為能被虛擬機器接受的Class檔案。使用魔數而不是擴充名來標記Class檔案是因為擴充名可以隨時被修改。
緊接著魔數的4個位元組儲存的是Class檔案的版本號。高版本的JDK可以向下相容低版本的Class檔案,而不可以執行以後版本的Class檔案。

常量池

緊接著版本號的是常量池入口,常量池可理解為Class檔案之中的資源倉庫,它是Class檔案中與其他專案關聯最多的資料型別,也是佔用Class檔案空間最多的資料專案,同時也是Class檔案中第一個出現的表型別資料專案。
由於常量池中常量池中常量的數量是不固定的,所以在常量池的入口放置一項u2型別的資料,用來記錄常量池中常量的數量。
常量池主要存放兩大類常量:字面量和符號引用。

  • 字面量
    字面量比較接近Java語言層面的常量,如文字字串、final定義的常量等
  • 符號引用
    符號引用屬於編譯原理方面的概念,包括下面三類常量:
  1. 類和介面的全限定名
  2. 欄位的名稱和描述符
  3. 方法的名稱和描述符

Java程式碼在進行javac編譯的時候,沒有像C或C++那樣有連線這一步驟,而是在虛擬機器載入Class檔案時候進行動態連線。也就是說,Class檔案不會儲存各個方法和欄位的最終記憶體佈局資訊。因此這些欄位、方法的符號引用不經過執行期轉換的話無法得到真正的記憶體入口地址,也就無法給虛擬機器使用。當虛擬機器執行時,需要從常量池獲得對應的符號引用,再在類建立時解析到具體的記憶體地址。(通俗地說,只有類在載入時,被分配具體的記憶體空間,符號引用才會解析到具體對應的記憶體地址,變為直接引用,否則Class檔案只存在符號引用;或者說呼叫一個類變數或類方法時,我們需要知道其在記憶體中的具體位置,才可以呼叫成功,所以類沒有載入時,是無法知道其記憶體地址的,符號引用是無法呼叫成功的)

類或介面表

深入理解Java虛擬機器 – 類檔案結構
  • tag
    tag是標記位用於區分常量型別。一下是14種常量型別代表的具體含義:

    深入理解Java虛擬機器 – 類檔案結構


  • name_index
    name_index是一個索引值,指向常量池中一個CONSTANT_Utf8_info型別常量,此常量代表這個類或介面的全限定名。

其他的表如方法表、屬性表等都類似,這裡不一一細說。

相關文章