《深入理解Java虛擬機器》讀書筆記3__Java

來源:互聯網
上載者:User

一、平台無關性和語言無關性

        位元組碼(ByteCode)是Java構建平台無關性和語言無關性的基石。

        平台無關性是指不同的CPU指令集、不同的作業系統,都能識別相同的位元組碼,實現“一次編寫,到處運行(Write Once, Run Anywhere)”。

        語言無關性是指不同的開發語言編譯的Class檔案,只要符合Class檔案應有的結構就可以在Java虛擬機器上運行。

        java正是為了構建平台無關性和語言無關性,所以才將Java規範分為:Java語言規範《The Java Language Specification》和Java虛擬機器規範《The Java Virtual Machine Specification》。

        Java語言無關性的示意圖:

二、類檔案結構

      Class檔案是一組以8位位元組為基礎單位的二進位流。大於8位元組的資料項目,高位在前地位在後。

      Class檔案中只有兩種資料類型:無符號數和表

      其中,以u1、u2、u4、u8來分別代表1個位元組、2個位元組、4個位元組和8個位元組的無符號數,可以描述數字、索引引用、數量值、UTF-8編碼構成的字串值。

表用於描述有層次關係的複合結構的資料,類型有:cp_info、field_info、method_info、attribute_info

註:描述同一類型但數量不定的資料時,使用一個前置容量計數器加若干個連續的資料項目的形式。

1、魔數

     每個Class檔案的頭4個位元組稱為魔數(Magic Number),它的唯一作用是用於確定這個檔案是否是一個能被虛擬機器接受的Class檔案。(身份識別)

     Class檔案的魔數值為:0xCAFEBABE (“咖啡館寶貝”)。

2、Class檔案的版本號碼

     緊接著魔數的4個位元組儲存Class檔案的版本號碼,其中第5和第6個位元組是次版本號碼(Minor Version),第7和第8個位元組是主要版本號(Major Version)。

註:高版本的JDK能向下相容以前的版本的Class檔案,但不能運行以後版本的Class檔案。Java版本號碼從45開始,JDK1.0~1.1使用了45.0~45.3的版本號碼,JDK1.2能使用46.0~46.65535的版本號碼,同理JDK1.6能使用50.0~50.65535的版本號碼。

3、常量池

     緊接著主次版本號碼之後的是常量池入口,由於常量池中的常量的數量是不固定的,所以常量池入口放置的是一項u2類型的資料,代表常量池容量計數值(constant_pool_count),該容量計數從1開始,常量池中常量數等於計數值減一,索引為0的常量做特殊用處。

註:Class檔案中只有常量池的容量計數是從1開始的,對於其他集合類型,包括介面索引集合、欄位表集合、方法表集合等的容量計數都是從0開始的。

       【】 常量池之中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。

       字面量:接近於Java語言層面的常量概念,比如文本字串、被聲明為final的常量值等。

       註:對於字面常量,常量池不僅會儲存該類本身定義的字面常量,還會存放該類引用到的其他類的字面常量,這樣做是對訪問的一種最佳化,比如筆記4中“被動引用”的第三個例子。

       符號引用包含以下三類常量:

              ** 類和介面的全限定名(Fully Qualified Name)

              ** 欄位的名稱和描述符(Descriptor)

              ** 方法的名稱和描述符

        【】常量池中的每一項常量都是一個表,表開始的第一位是一個u1類型的標誌位,代表當前這個常量屬於哪種常量類型,共有11中結構各不相同的表結構資料,類型如下:

11種資料類型的結構定義如下:

4、訪問標誌

      在常量池之後,緊接著的2個位元組代表訪問標誌(access_flags),這個標識用於識別一些類或介面層次的訪問資訊。

標誌位及標誌的含義如下:

註:access_flags中一共有32個標誌位可以使用,當前只定義了其中的8個,沒有使用到的標誌位要求一律為0。

5、類索引、父類索引和介面索引集合

      緊接著訪問標誌之後的是按順序排列的類索引(this_class)、父類索引(super_class)、介面索引集合(interfaces)。

      類索引和父類索引都是一個u2類型的資料。

      介面索引集合是一組u2類型的資料的集合。

      Class檔案由這三項資料來確定這個類的繼承關係。類索引用於確定這個類的全限定名,父類索引用於確定這個類的父類的全限定名(註:除了Object類外,所有Java類的父類索引都不為0)。介面索引集合用來描述這個類實現了哪些介面(實現的介面會按照聲明順序從左至右排列在介面的索引集合中)。

      類索引、父類索引引用的u2類型的索引值分別指向一個類型為CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info類型的常量中的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字串。類索引尋找全限定名的過程如下:

        介面索引集合,入口的第一項是u2類型的資料代表介面計數器(interfaces_count),表示索引表的容量。

6、欄位表集合

       欄位表(field_info)用於描述介面或類中聲明的變數。欄位包含了類級變數或執行個體級變數,但不包括在方法內部聲明的變數。(注意每個欄位就是一張表)

       欄位可以包含的資訊:欄位的存取權限、是否是static變數(類級地)、是否是可變的(final)、並發可見度(volatile)、是否可序列化(transient)、欄位資料類型(基本類型、對象、數組)、欄位名稱。

       欄位表結構定義:

       access_flags表示欄位訪問標記,其定義如下:

 

        name_index是對常量池的引用,代表欄位的簡單名稱。簡單名字是相對於全限定名而言的。

        descriptor_index也是對常量池的引用,代表欄位和方法的描述符。

        方法和欄位的描述符是用來描述欄位的資料類型、方法的參數列表(數量、類型、順序)和傳回值。其描述規則如下:

【類型規則】:

         * 基礎資料型別 (Elementary Data Type) 和 void類型 都用一個大寫字元來表示;

         * 物件類型用L加對象的全限定名來表示;

         * 數群組類型,每一維度將使用一個前置的“[”字元來描述。例如定義一個java.lang.String[][]類型的二維數組,將被記錄為:“[[Ljava/lang/String”,一個int[]類型,將被記錄為“[I”.

註:類型描述元標識字元定義如下:

 

       attributes_count、attribute_info,表示欄位的屬性工作表集合,用於儲存一些額外的資訊。


       欄位表集合的第一個u2類型資料為容量計數器fields_count,,接著是一系列的欄位表。

       欄位表集合中不會列出從超類或父介面中繼承的欄位,但可能列出原本Java代碼中不存在的欄位,比如內部類中為了保持對外部類的訪問性,會自動添加指向外部類執行個體的欄位。


7、方法表集合

      方法表的結構和欄位表一樣,各個結構的含義類似,只有訪問標誌、屬性工作表集合的可選項有所不同。

方法表標識位的含義如下:

方法描述符的表示

【方法描述規則】:

        * 先描述參數列表,在描述傳回值;

        * 參數列表按照參數的嚴格順序放在一組小括弧“()內。如方法void inc()的描述符為“()V”,方法java.lang.String toString()的描述符為“()Ljava/lang/String;"(註:其中“;”表示全限定名結束)。方法int indexOf(char[] source, int offset)的描述符為“([CI)I"。


8、屬性工作表集合

      屬性工作表(attribute_info)用於描述某些情境專有的資訊,Class檔案本身、欄位表、方法表中都有自己的屬性工作表集合。

      屬性工作表不要求嚴格的順序,任何人實現的編譯器都可以向屬性工作表中寫入自己定義的屬性資訊,java虛擬機器會忽略掉它不認識的屬性。

虛擬機器規範預定義的屬性如下:

       對於每個屬性,它的名稱需要從常量池中引用一個CONSTANT_Utf8_info類型的常量來表示。

屬性工作表的結構定義如下:

<1>【Code屬性工作表】

        Java程式方法體裡面的代碼經過編譯器處理之後,最終會變成位元組碼指令,這些指令儲存在Code屬性工作表內,Code屬性可以出現在方法表的屬性集合中,介面或抽象類別的方法不存在Code屬性。

       Code屬性工作表的結構如下:

       attribute_name_index是一項指向CONSTANT_Utf8_info型常量的索引,常量值固定為“Code”,代表該屬性的屬性名稱;

       attribute_length指示屬性值的長度。屬性值的長度 固定為整個屬性工作表的長度減去6個位元組(註:這6個位元組是指attribute_name_index和attribute_length項的長度總和)。

       max_stack:代表運算元棧(Operand Stacks)深度的最大值。註:虛擬機器運行時需要該值來分配棧幀(Frame)中的操作棧深度。

       max_locals:代表局部變數表所需的儲存空間,max_locals的單位是Slot。

註:Slot是虛擬機器為局部變數分配記憶體所使用的最小單位,對於長度不超過32位的資料類型,每個局部變數佔用1個Slot,而double和long這兩種64位的資料類型則需要2個Slot來存放。

註:方法參數(包含執行個體方法隱含的this)、顯式異常處理器參數、方法體中定義的局部變數都使用局部變數來存放。

       code_length和code用來儲存位元組碼指令。code_lengh代表位元組碼長度,code是儲存位元組碼指令的一系列位元組流。

註:每條指令都是一個u1類型的單位元組。

註:虛擬機器規範限制了一個方法不能超過65535條位元組碼指令。超過了,Javac編譯器就會拒絕編譯。

       execption_table_length和exception_table是顯式異常處理表集合,顯式異常處理表對於Code屬性來說並不是必須存在的。

顯式異常處理表結構的定義如下:

這些欄位的含義是:如果位元組碼從第start_pc行(註:這裡的“行”,是指位元組碼相對於方法體開始的位移量)到第end_pc行之間(不包含第end_pc行)出現了類型為catch_type代表的異常或其子類型的異常,則轉到第handler_pc行繼續處理。當catch_type的值為0時,代表任何的異常情況都需要轉向到handler_pc處進行處理。

<2>【Exceptions屬性】

Exceptions屬性的作用是列舉出方法中可能拋出的受查異常(Checked Exceptions),也就是方法描述時在throws關鍵字後面列舉的異常。

Exceptions屬性的結構如下:

number_of_exception項表示可能拋出的受查異常數量;

exception_index_table項表示一個指向常量池中CONSTANT_Class_info型常量的索引,代表該受查異常的類型。

<3>【LineNumberTable屬性】

        LineNumberTable屬性用於描述Java源碼行號與位元組碼行號(位元組碼的位移量)之間的對應關係。它不是運行時必需的屬性。在Javac中使用-g:none或-g:lines選項來取消或要求產生這項資訊。如果不產生這項資訊,對程式運行產生的最主要的影響就是在拋出異常時,堆棧中將不會顯示出錯的行號,調試時無法按照源碼來設定斷點。

LineNumberTable屬性的結構如下:

其中line_number_table是一個數量為line_number_table_length、類型為line_number_info的集合,line_number_info表包括了start_pc和line_number兩個u2類型的資料項目,前者是位元組碼行號,後者是Java原始碼行號。

<4>【LocalVariableTable屬性】

LocalVariableTable屬性用於描述棧幀中局部變數表中的變數與Java原始碼中定義的變數之間的關係。不是運行時必需的屬性,預設也不會產生到Class檔案之中,可以在Javac中使用-g:none或-g:vars選項來控制。

LocalVariableTable屬性的結構定義如下:

其中loca_variable_info項代表一個棧幀與源碼中的局部變數的關聯,其結構定義如下:

其中,start_pc和length屬性分別代表這個局部變數的聲明周期開始的位元組碼位移量以及其作用範圍覆蓋的長度,(兩者合起來就是局部變數在位元組碼中的範圍範圍);

        name_index和descriptor_index都是指向常量池中CONSTANT_Utf8_info型常量的索引,分別代表了局部變數的名稱及該局部變數的描述符。

        index是這個局部變數在棧幀局部變數表中Slot的位置。當該變數為64位類型時,它佔用的是Slot為index和index+1兩個位置。

註:Java引入泛型後,增加了"LocalVariableTypeTable"屬性。結構與LocalVariableTable類似,只有一個屬性項不同,就是將descriptor_index替換為Signature(欄位的特徵簽名)。

<5>【SourceFile屬性】

        SourceFile屬性用於記錄產生這個Class檔案的源碼檔案名稱。該屬性也是可選的,使用Javac的-g:none或-g:source選項來控制。

該屬性的結構定義如下:


其中,sourcefile_index項是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源碼檔案的檔案名稱。

<6>【ConstantValue屬性】

ConstantValue屬性的作用是通知虛擬機器自動為靜態變數賦值。

       Java中對於非static類型的變數(也就是執行個體變數)的賦值是在執行個體構造器<init>方法中進行的;對於類變數,則有兩種方式可以選擇:在類構造器<clinit>方法中進行賦值,或者使用ConstantValue屬性來賦值。Sun Javac編譯器的選擇是:對於final和static同時修飾,並且類型為基本類型或String類型的變數,產生ConstantValue屬性來初始化,其餘情況的static變數通過<clinit>方法來初始化。

ConstantValue屬性的結構定義如下:

constantvaue_index項代表了常量池中一個字面量常量的引用,根據欄位類型的不同,字面量可以是CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_Integer_info和CONSTANT_String_info常量中的一種。

<7>【InnerClasses屬性】

InnerClasses屬性用於記錄內部類與宿主類之間的關聯。如果一個類中定義了內部類,那編譯器將會為它及它所包含的內部類產生InnerClasses屬性。

InnerClasses屬性的結構如下:

資料項目number_of_classes代表需要記錄多少個內部類資訊,每個內部類的資訊都由一個inner_classes_info表進行描述。

inner_classes_info表的結構如下:

inner_class_info_index和outer_info_index都是指向常量池中CONSTANT_Class_info型常量的索引,分別代表了內部類和宿主類的符號引用。

inner_name_index是指向常量池中CONSTANT_Utf8_info型常量的索引,代表這個內部類的名稱,如果是匿名內部類,則這項值為0。

inner_class_access_flags是內部類的訪問標誌,類似於類的access_flags,它的取值範圍如下:

<8>【Deprecated及Synthetic屬性】

      Deprecated和Synthetic兩個屬性都屬於標誌類型的布爾屬性,只存在有或者沒有的區別,沒有屬性值的概念。

      Deprecated屬性用於表示某個類、欄位或方法,已經被程式作者定為不再推薦使用(在代碼中使用@deprecated註解來設定)。

      Synthetic屬性代表此欄位或方法並不是有Java源碼直接產生的,而是由編譯器自行添加的。

Deprecated和Synthetic屬性的結構定義如下:

其中attribute_length資料項目的值必須為0x00000000。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.