【JVM】class文件結構1——魔數、版本號、常量池與類訪問標記

1、魔數

我們可以利用editplus,以16進制的編碼格式來查看class文件的結構,具體操作方法爲在editplus的工具欄中點擊Edit,下拉選擇Hex Viewer即可。

如圖所示,前四(4個bit位*8個字母=32,32/8=4字節)個字節爲0xCAFEBABE,這就是class文件的魔數。

虛擬機藉助魔數,用來識別.class 文件,虛擬機在加載類文件之前會先檢查魔數,如果不是 0xCAFEBABE 則拒絕加載該文件。

關於class文件魔數的由來,可以參考這篇文章class文件魔數CAFEBABE的由來


2、版本號

版本號分爲副版本號(minor version)與主版本號(major version),緊隨魔數之後。

可以看到主版本號爲52(3*16+4),52對應的java版本爲java8,規律就是java版本=主版本號-44。例如主版本號50對應的java版本爲java6。

如果java6的虛擬機去加載一個java8編譯的類,則虛擬機直接會拋出java.lang.UnsupportedClassVersionError。

我們使用javap -v,也可以直接看到class文件的主副版本號:


3、常量池

常量池緊隨着版本號,是class文件中最爲複雜的部分。

當執行一個java方法時,需要將操作數入棧,這個時候如果操作數很小,那麼直接內嵌到字節碼中。如果是比較大的數字或者是字符串時,就不再會內嵌到字節碼中,而是存到常量池中。當將這些操作數入棧時,字節指令後面會跟着一個指向常量池的一個索引。

比如這個方法:

    public void print() {
        System.out.println(1);
        System.out.println("abcd");
    }

對應字節碼爲:

當然,常量池不僅僅存儲字符串類型,完整的常量類型,如下表所示:

類型

說明

CONSTANT_Utf8_info

表示utf-8編碼的字符串常量

CONSTANT_Integer_info

表示int常量

CONSTANT_Float_info

表示float常量

CONSTANT_Long_info

表示long常量

CONSTANT_Double_info

表示double常量

CONSTANT_Class_info

表示類或接口的完全限定名

CONSTANT_String_info

表示java.lang.String類型的字符串

CONSTANT_Fieldref_info

表示字段的符號引用

CONSTANT_Methodref_info

表示方法的符號引用

CONSTANT_InterfaceMethodref_info

表示實現的接口方法的符號引用

CONSTANT_NameAndType_info

表示字段或方法的名稱以及類型

CONSTANT_MethodHandle_info

表示方法句柄

CONSTANT_MethodType_info

表示方法類型

CONSTANT_InvokeDynamic_info

表示動態調用

符號引用可以這麼去理解:符號引用是一個具有定位意義的字符串,在類加載過程中,連接的子過程解析階段時,虛擬機會將符號引用解析爲直接引用。關於類加載機制,可以先參考我的另外一篇文章類的奇幻漂流——類加載機制探祕

就以我們最經常用到的System.out.println()方法爲例,out是System類中的一個PrintStream類型的引用變量,println則是PrintStream類中的一個方法,那麼out字段的符號引用與println方法的符號引用是什麼樣子的呢?

以下面的代碼爲例:

package com.yang.testMethod;

public class Main {
    public static void main(String[] args) {
        System.out.println(1);
    }
}

常量池如下:

可以看得出來,out字段的Fieldref=Class+NameAndType,即字段的符號引用=所屬類的符號引用+字段的描述符。

println方法的符號引用也是同樣的組成方式,但方法的NameAndType包含參數類型描述符以及返回值類型描述符。

描述符又是怎樣組織的,可以先看字段表中的字段描述符以及方法表中的方法描述符。

MethodHandle、MethodType與InvokeDynamic是爲了支持動態語言調用,在1.7之後才加入的,這裏不做討論。不過這裏的invokeDynamic很有意思,會另外篇幅進行介紹。


4、類訪問標記

類訪問標記緊隨在常量池之後,佔兩個字節,一共16位,目前只使用了其中8位。

虛擬機在編譯某個類時,會解析出這個類的特性,將其設置到類訪問標記上,即將特定的bit位置1,表示該類擁有這個bit位上代表的標記。

8種標記如下表所示,例如編譯一個public類時,該類的訪問標記上會有ACC_PUBLIC與ACC_SUPER。

標記名稱

說明

ACC_PUBLIC

類或接口的訪問權限爲public

ACC_FINAL

類被final修飾

ACC_SUPER

ACC_INTERFACE

接口

ACC_ABSTRACT

抽象類或接口

ACC_SYNTHETIC

編譯器自動生成,不是用戶對代碼編譯生成

ACC_ANNOTATION

註解

ACC_ENUM

枚舉

例如,有這樣的一個java文件:

package com.yang.testFlag;

public interface Main {
}

使用javac Main.java,接着javap -v Main之後,得到該接口的訪問標記爲:

接口是一種特殊的抽象類,所有的變量都爲public static final類型,所有的方法都是抽象方法。(當然除了靜態方法與默認方法)。更多關於抽象類與接口的特徵與區別,可以先參考我的另外一篇文章抽象類和接口的聯繫與區別

因此,一個public類型的接口,它的訪問標記有3個,分別爲ACC_PUBLIC、ACC_INTERFACE與ACC_ABSTRACT。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章