深刻理解java虛擬機【Java Class類文件結構】

Java語言從誕生之時就宣稱一次編寫,處處運行的跨平臺特性,其實現原理是源碼文件並無直接編譯成機器指令,而是編譯成Java虛擬機能夠識別和運行的字節碼文件(Class類文件,*.class),字節碼文件是一種平臺無關的中間編譯結果,字節碼文件由java虛擬機讀取,解析和執行,java虛擬機屏蔽了不一樣操做系統和硬件平臺的差別性。java

現在的java虛擬機已經稱爲一種通用平臺,不但可以運行java語言,Groovy,JRuby,Jython等一大批動態語言也能夠直接在Java虛擬機上運行,其原理也是這些動態語言的編譯器將源碼文件編譯爲和Java相同的字節碼文件,這樣Java虛擬機就能夠像執行java語言同樣執行這些動態語言了。web

字節碼class類文件是由一系列字節碼命令組成,用於表示程序中各類常量、變量、關鍵字和運算符號的語義等等。Java的Class類文件是一組以8爲字節爲單位的二進制流,各個數據項嚴格按照順序緊湊地排列在Class類文件之中,中間沒有添加任何分隔符,當遇到須要佔用8位字節以上空間的數據項時,按照高位在前的方式分割成若干個8位字節進行存儲。安全

Java虛擬機規定,Class類文件格式採用相似C語言結構體的僞結構來存儲,這種僞結構中只有兩種數據類型:無符號數和表:併發

(1).無符號數:app

屬於基本類型的數據,以u1, u2, u4, u8來分別表明1個字節,2個字節,4個字節和8個字節的無符號數,無符號數能夠用來描述數字、索引引用、數量值或者按照UTF-8編碼的字符串值。編輯器

(2).表:ide

由多個無符號數或其餘表做爲數據項構成的複合數據類型,因此表都習慣性地以「_info「結尾。表用於描述有層次關係的複合結構數據,整個Class文件本質就是一張表。函數

Java Class類文件結構以下:工具

類型oop

名稱

數量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count-1

u2

access_flags

1

u2

this_class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

method_info

methods

methods_count

u2

attributes_count

1

attribute_info

attributes

attributes_count

Class類文件沒有任何分隔符,是嚴格按照這個結構表順序排列,下面具體介紹各個名稱含義:

(1).magic:

每一個Class文件的頭4個字節被稱爲魔數,它的惟一做用是用於肯定這個文件是否爲一個能被java虛擬機所接收的Class類文件,即用於斷定文件是不是符合規範的java Class文件。雖說後綴名「.class」能夠代表文件是一個Class文件,可是文件後綴名是能夠隨意被改動的,基於安全的考慮,不少文件都經過魔數值來惟一肯定文件類型,java的Class文件魔數是:0xCAFEBABE.

(2).minor_version和major_version:

每一個Class文件的第5和第6個字節表明Class文件的次版本號,第7和第8個字節表明Class文件的主版本號。

Class文件的主、次版本號是由JDK決定的,JDK1.0~JDK1.1使用了45.0~45.3的版本號(45是主版本好,點」.「以後的是次版本號),從JDK1.1開始,每一個大版本的JDK主版本號加1.

Class主、次版本號的一個做用時,高版本的Java虛擬機能夠向前兼容,運行低版本JDK編譯的Class字節碼文件,而低版本的java虛擬機不能運行高版本JDK編譯的Class字節碼文件。當低版本的java虛擬機運行高版本JDK編譯的Class字節碼文件時,一般會報相似以下的異常:

[java]  view plain copy
 
  1. Exception in thread "main" java.lang.UnsupportedClassVersionError: a (Unsupporte  
  2. d major.minor version 49.0)  

 

JDK1.0~JDK1.1使用了45.0~45.3的版本號,JDK1.2使用了46.0~46.65535的版本號,JDK1.3使用了47.0~47.65535的版本號,JDK1.4使用了48.0~48.65535的版本號,JDK1.5使用了49.0~49.65535的版本號,JDK1.6使用了50.0~50.65535的版本號,JDK1.7使用51.0~51.65535的版本號。

在編譯時能夠經過指定-target參數來改變主版本號,如JDK1.6編譯時若是沒有給定target參數,則編譯出來的Class文件的主版本號是50,若是給定」-target 1.4 -source 1.4」參數以後,則主版本將變爲48,若是給定」-target 1.5 」參數以後,則主版本將變爲49。

(3). constant_pool_count和constant_pool:

constant_pool_count表明Class文件中常量池的數目,因爲常量池的計數從1開始,所以常量池的容量是constant_pool_count-1。

第0項常量空出作特殊考慮,爲了知足一些指向常量池的索引值在某些特定狀況下須要表達「不指向任何一個常量池」的意思。

constant_pool常量池是Class類文件中出現的第一個表類型數據,常量池主要存放兩大類常量:

a.字面量(Literal):包括文本字符串、final類型常量值。

b.符號引用(SymbolicReferences):包括類和接口的全限定名、字段的名稱和描述符、方 法的名稱和描述符。

(4). access_flags:

用於表示Class或接口層次的訪問標誌,即類或接口層面的訪問控制信息,一般存儲的信息包括:Class類文件是類、接口、枚舉或是註解;是否認義爲public類型;是否認義爲abstract類型;類是否被定義爲final等等。

(5). this_class、super_class和interfaces:

this_class類索引用於肯定類的全限定名,super_class父類索引用於肯定父類的全限定名,interfaces接口索引用於肯定接口的全限定名,因爲java中能夠實現多個接口,所以使用interfaces_count來存儲接口數量。

(6). field:

field_info字段表用於描述接口或者類中聲明的變量,field字段包括了類級變量(靜態變量)和實例級變量(成員變量),但不包括方法內部的局部變量。

fields_count字段數目表示Class文件中的類和實例變量總數,字段存放的信息包括:字段訪問標誌、是否靜態、是否final、是否併發可見volatile、是否可序列化transient、數據類型、字段名稱等等。

注意:字段表中不包含從父類或者接口中繼承而來的字段,可是會添加本來代碼中不存在的字段,例如this,以及內部類對外部類訪問而自動添加的外部類實例字段等。

(7).method:

method_info方法表用於描述類或者接口中聲明的方法,methods_count用於表示Class文件中方法總數,method方法存儲了方法的訪問標識、是否靜態、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值類型、方法名稱、方法參數列表等信息。

方法的代碼指令並無直接存放在方法表中,而是存放着屬性表中的方法表Code中。

注意:若是父類的方法在子類沒有被重寫,方法表中不會出現來自父類的方法信息,可是編譯器會自動添加類構造器」<clinit>」方法和實例構造器」<init>」方法。

Java編譯器的方法特徵簽名只包括:方法名稱、參數順序和參數類型,不包括方法返回值類型,所以java的方法重載不能經過方法的返回值類區別,可是在Class文件中,方法特徵簽名包括方法的返回值類型,所以Class文件中能夠共存兩個名稱和參數徹底相同而返回值類型不一樣的方法。

(8). attribute:

attribute_info屬性表是Class文件格式中最具擴展性的一種數據項目,用於存放field_info字段表、method_info方法表以及Class文件的專有信息,屬性表不要求各個屬性有嚴格順序,只要求不與已有的屬性名字重複便可,屬性表中存放的經常使用信息以下:

屬性名稱

使用位置

含義

Code

方法表

Java代碼編譯後的字節碼指令

ConstantValue

字段表

final關鍵字定義的常量值

Deprecated

類、方法表、字段表

被聲明爲Deprecated的字段或方法

Exception

方法表

方法拋出的異常

InnerClasses

類文件

內部類列表

LineNumberTable

Code屬性

java源碼行號和字節碼指令的對應關係

LocalVariableTable

Code屬性

方法的局部變量描述

SourceFile

類文件

源文件名稱

Synthetic

類、方法表、字段表

標識方法或字段爲編譯器自動生成

Class文件是二進制文件,使用支持二進制的文本編輯器打開以後顯示的全是二進制數據,很是的不便於閱讀和理解,使用JDK提供的javap工具能夠簡單將Class反編譯,編譯理解Class文件的結構,例子以下:

源碼:

[java]  view plain copy
 
  1. public class Test {    
  2.     public int getNum(int i) {    
  3.         return i + 1;    
  4.     }    
  5. }    

javap反編譯以後的字節碼文件:

[java]  view plain copy
 
  1. public class Test extends java.lang.Object    
  2.   SourceFile: "Test.java"    
  3.   minor version: 0    
  4.   major version: 50    
  5. //常量池  
  6.  Constant pool:    
  7. const #1 = class        #2;       
  8. const #2 = Asciz        Test;    
  9. const #3 = class        #4;       
  10. const #4 = Asciz        java/lang/Object;    
  11. const #5 = Asciz        <init>;  //實例構造器  
  12. const #6 = Asciz        ()V;  //void返回類型  
  13. const #7 = Asciz        Code;  //屬性表Code屬性  
  14. const #8 = Method       #3.#9;  //方法特徵簽名  java/lang/Object."<init>":()V    
  15. const #9 = NameAndType  #5:#6;//  方法名稱和返回值"<init>":()V    
  16. const #10 = Asciz       LineNumberTable;  //屬性表源碼行號和字節碼指令對應表  
  17. const #11 = Asciz       LocalVariableTable;  //屬性表方法局部變量表  
  18. const #12 = Asciz       this;  //Test類實例對象自己  
  19. const #13 = Asciz       LTest;;  //對象類型,Test類  
  20. const #14 = Asciz       getNum;  //方法名稱  
  21. const #15 = Asciz       (I)I;  //方法參數列表爲一個int類型和返回值爲int類型  
  22. const #16 = Asciz       i;  //參數名稱i  
  23. const #17 = Asciz       I;  //參數類型int  
  24. const #18 = Asciz       SourceFile;    
  25. const #19 = Asciz       Test.java;    
  26.  //方法表  
  27. {    
  28. //構造函數(默認構造方法)  
  29. public Test();    
  30.   Code:  //屬性表Code屬性  
  31.    Stack=1, Locals=1, Args_size=1    
  32.    0:   aload_0    
  33.    1:   invokespecial   #8//Method java/lang/Object."<init>":()V    
  34.    4:   return    
  35.   LineNumberTable:    
  36.    line 20    
  37.     
  38.   LocalVariableTable:  //屬性表方法局部變量表  
  39.    Start  Length  Slot  Name   Signature    
  40.    0      5      0    this       LTest;    
  41.     
  42. //自定義方法  
  43. public int getNum(int);    
  44.   Code:    
  45.    Stack=2, Locals=2, Args_size=2    
  46.    0:   iload_1    
  47.    1:   iconst_1    
  48.    2:   iadd    
  49.    3:   ireturn    
  50.   LineNumberTable:    
  51.    line 40    
  52.     
  53.   LocalVariableTable:    
  54.    Start  Length  Slot  Name   Signature    
  55.    0      4      0    this       LTest;    
  56.    0      4      1    i       I    
  57. }    
相關文章
相關標籤/搜索