《Java虛擬機原理圖解》 1.1、class文件基本組織結構

[last updated: 2014/11/19 09:06]         

      作爲Java程序猿,我們知道,我們寫好的.java 源代碼,最後會被Java編譯器編譯成後綴爲.class的文件,該類型的文件是由字節組成的文件,又叫字節碼文件。那麼,class字節碼文件裏面到底是有什麼呢?它又是怎樣組織的呢?讓我們先來大概瞭解一下他的組成結構吧。


NO1. 魔數(magic)

      所有的由Java編譯器編譯而成的class文件的前4個字節都是“0xCAFEBABE”  
      它的作用在於:當JVM在嘗試加載某個文件到內存中來的時候,會首先判斷此class文件有沒有JVM認爲可以接受的“簽名”,即JVM會首先讀取文件的前4個字節,判斷該4個字節是否是“0xCAFEBABE”,如果是,則JVM會認爲可以將此文件當作class文件來加載並使用。


NO2.版本號(minor_version,major_version)

      隨着Java本身的發展,Java語言特性和JVM虛擬機也會有相應的更新和增強。目前我們能夠用到的JDK版本如:1.5,1.6,1.7,還有現如今最新的1.8。發佈新版本的目的在於:在原有的版本上增加新特性和相應的JVM虛擬機的優化。而隨着主版本發佈的次版本,則是修改相應主版本上出現的bug。我們平時只需要關注主版本就可以了。

主版本號和次版本號在class文件中各佔兩個字節,副版本號佔用第5、6兩個字節,而主版本號則佔用第7,8兩個字節。JDK1.0的主版本號爲45,以後的每個新主版本都會在原先版本的基礎上加1。若現在使用的是JDK1.7編譯出來的class文件,則相應的主版本號應該是51,對應的7,8個字節的十六進制的值應該是 0x33。

      一個 JVM實例只能支持特定範圍內的主版本號 (MiMj) 和 0 至特定範圍內 (0 至 m) 的副版本號。假設一個 Class 文件的格式版本號爲 V, 僅當Mi.0 ≤ v ≤ Mj.m成立時,這個 Class 文件纔可以被此 Java 虛擬機支持。不同版本的 Java 虛擬機實現支持的版本號也不同,高版本號的 Java 虛擬機實現可以支持低版本號的 Class 文件,反之則不成立。

     JVM在加載class文件的時候,會讀取出主版本號,然後比較這個class文件的主版本號和JVM本身的版本號,如果JVM本身的版本號 < class文件的版本號,JVM會認爲加載不了這個class文件,會拋出我們經常見到的"java.lang.UnsupportedClassVersionError: Bad version number in .class file " Error 錯誤;反之,JVM會認爲可以加載此class文件,繼續加載此class文件。

     

    小貼士:

1. 有時候我們在運行程序時會拋出這個Error 錯誤:"java.lang.UnsupportedClassVersionError: Bad version number in .class file"。上面已經揭示了出現這個問題的原因,就是在於當前嘗試加載class文件的JVM虛擬機的版本 低於class文件的版本。解決方法:1.重新使用當前jvm編譯源代碼,然後再運行代碼;2.將當前JVM虛擬機更新到class文件的版本。

2. 怎樣查看class文件的版本號?

 可以藉助於文本編輯工具,直接查看該文件的7,8個字節的值,確定class文件是什麼版本的。

當然快捷的方式使用JDK自帶的javap工具,如當前有Programmer.class 文件,進入此文件所在的目錄,然後執行 ”javap -v Programmer“,結果會類似如下所示:

    



NO3.常量池計數器(constant_pool_count)

 常量池是class文件中非常重要的結構,它描述着整個class文件的字面量信息。 常量池是由一組constant_pool結構體數組組成的,而數組的大小則由常量池計數器指定。常量池計數器constant_pool_count 的值 =constant_pool表中的成員數+ 1。constant_pool表的索引值只有在大於 0 且小於constant_pool_count時纔會被認爲是有效的。

NO4.常量池數據區(constant_pool[contstant_pool_count-1])

常量池,constant_pool是一種表結構,它包含 Class 文件結構及其子結構中引用的所有字符串常量、 類或接口名、字段名和其它常量。 常量池中的每一項都具備相同的格式特徵——第一個字節作爲類型標記用於識別該項是哪種類型的常量,稱爲 “tag byte” 。常量池的索引範圍是 1 至constant_pool_count−1。常量池的具體細節我們會稍後討論。


NO6.訪問標誌(access_flags)

       訪問標誌,access_flags 是一種掩碼標誌,用於表示某個類或者接口的訪問權限及基礎屬性。

      




NO7.類索引(this_class)

       類索引,this_class的值必須是對constant_pool表中項目的一個有效索引值。constant_pool表在這個索引處的項必須爲CONSTANT_Class_info 類型常量,表示這個 Class 文件所定義的類或接口。



NO8.父類索引(super_class)

     父類索引,對於類來說,super_class 的值必須爲 0 或者是對constant_pool 表中項目的一個有效索引值。如果它的值不爲 0,那 constant_pool 表在這個索引處的項必須爲CONSTANT_Class_info 類型常量,表示這個 Class 文件所定義的類的直接父類。當前類的直接父類,以及它所有間接父類的access_flag 中都不能帶有ACC_FINAL 標記。對於接口來說,它的Class文件的super_class項的值必須是對constant_pool表中項目的一個有效索引值。constant_pool表在這個索引處的項必須爲代表 java.lang.Object CONSTANT_Class_info 類型常量 。如果 Class 文件的 super_class的值爲 0,那這個Class文件只可能是定義的是java.lang.Object類,只有它是唯一沒有父類的類。



NO9.接口計數器(interfaces_count)

      接口計數器,interfaces_count的值表示當前類或接口的直接父接口數量。



NO10.接口信息數據區(interfaces[interfaces_count])

      接口表,interfaces[]數組中的每個成員的值必須是一個對constant_pool表中項目的一個有效索引值, 它的長度爲 interfaces_count。每個成員 interfaces[i]  必須爲 CONSTANT_Class_info類型常量,其中 0 ≤ i <interfaces_count。在interfaces[]數組中,成員所表示的接口順序和對應的源代碼中給定的接口順序(從左至右)一樣,即interfaces[0]對應的是源代碼中最左邊的接口。



NO11.字段計數器(fields_count)

      字段計數器,fields_count的值表示當前 Class 文件 fields[]數組的成員個數。 fields[]數組中每一項都是一個field_info結構的數據項,它用於表示該類或接口聲明的類字段或者實例字段。


NO12.字段信息數據區(fields[fields_count])

      字段表,fields[]數組中的每個成員都必須是一個fields_info結構的數據項,用於表示當前類或接口中某個字段的完整描述。 fields[]數組描述當前類或接口聲明的所有字段,但不包括從父類或父接口繼承的部分。



NO13.方法計數器(methods_count)

     方法計數器, methods_count的值表示當前Class 文件 methods[]數組的成員個數。Methods[]數組中每一項都是一個 method_info 結構的數據項。


NO14.方法信息數據區(methods[methods_count])

      方法表,methods[] 數組中的每個成員都必須是一個 method_info 結構的數據項,用於表示當前類或接口中某個方法的完整描述。如果某個method_info 結構的access_flags 項既沒有設置 ACC_NATIVE 標誌也沒有設置ACC_ABSTRACT 標誌,那麼它所對應的方法體就應當可以被 Java 虛擬機直接從當前類加載,而不需要引用其它類。 method_info結構可以表示類和接口中定義的所有方法,包括實例方法、類方法、實例初始化方法方法和類或接口初始化方法方法 。methods[]數組只描述當前類或接口中聲明的方法,不包括從父類或父接口繼承的方法。



NO15.屬性計數器(attributes_count)

     屬性計數器,attributes_count的值表示當前 Class 文件attributes表的成員個數。attributes表中每一項都是一個attribute_info 結構的數據項。


NO16.屬性信息數據區(attributes[attributes_count])

     屬性表,attributes 表的每個項的值必須是attribute_info結構。

    在Java 7 規範裏,Class文件結構中的attributes表的項包括下列定義的屬性: InnerClasses  、 EnclosingMethod 、 Synthetic  、Signature、SourceFile,SourceDebugExtension 、Deprecated、RuntimeVisibleAnnotations 、RuntimeInvisibleAnnotations以及BootstrapMethods屬性。

      對於支持 Class 文件格式版本號爲 49.0 或更高的 Java 虛擬機實現,必須正確識別並讀取attributes表中的SignatureRuntimeVisibleAnnotationsRuntimeInvisibleAnnotations屬性。對於支持Class文件格式版本號爲 51.0 或更高的 Java 虛擬機實現,必須正確識別並讀取 attributes表中的BootstrapMethods屬性。Java 7 規範 要求任一 Java 虛擬機實現可以自動忽略 Class 文件的 attributes表中的若干 (甚至全部) 它不可識別的屬性項。任何本規範未定義的屬性不能影響Class文件的語義,只能提供附加的描述信息 。


根據上述的敘述,我們可以將class的文件組織結構概括成以下面這個結構體:






參考書目:

Java虛擬機規範(Java SE 7)中文版(Java_Virtual_Machine_Specification_Java_SE_7)

[深入理解Java虛擬機:JVM高級特性與最佳實踐].周志明



作者的話

    本文是《Java虛擬機原理圖解》的第一篇,如果您有興趣,請關注該系列的其他文章~

   覺得本文不錯,順手點個贊哦~~您的鼓勵,是我繼續分享知識的強大動力!




-----------------------------------------------------------------------------------------------------------------------------------------

                                                                                本文源自  http://blog.csdn.net/luanlouis/,如需轉載,請註明出處,謝謝!
發佈了60 篇原創文章 · 獲贊 2069 · 訪問量 105萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章