接上篇:http://www.javashuo.com/article/p-xdetuxcw-ky.htmljava
屬性表this
屬性表(attribute_info)在前面的講解之中已經出現過不少次,在Class文件中、字段表、方法表均可以攜帶本身的屬性表集合,以用於描述某些特定的場景信息。spa
與Class文件中其餘的數據項目要求嚴格的順序、長度和內容不一樣,屬性表集合的限制稍微寬鬆了一些,再也不要求各個屬性表具備嚴格順序,任何人實現的編譯器均可以向Class文件中寫入本身定義的屬性信息,只要不與已有的屬性名重複,Java虛擬機運行時會忽略掉它不認識的屬性,從這一點看,屬性表看似放開的限制,但這種放開是沒有意義的,至少在目前規則下是這樣的。Java虛擬機爲確保可以正確地解析Class文件,預約義了以下屬性:.net
虛擬機規範預約義的屬性調試
屬性名稱 | 使用位置 | 含義 |
Code | 方法表 | Java代碼編譯成的字節碼指令 |
ConstantValue | 字段表 | final關鍵字定義的常量(同時須要被static修飾) |
Deprecated | 類、方法表、字段表 | 被聲明爲deprecated的方法和字段 |
Exceptions | 方法表 | 方法拋出的異常 |
EnclosingMethod | 類文件 | 僅當一個類爲局部類或者匿名類時才能擁有這個屬性,這個屬性用於標識這個類所在的外圍方法 |
InnerClass | 類文件 | 內部類列表 |
LineNumberTable | Code屬性 | Java源代碼的行號與字節碼指令的對應關係 |
LocalVariableTable | Code屬性 | 方法的局部變量描述 |
StackMapTable | Code屬性 | JDK1.6中新增的屬性,供新的類型檢查驗證器(Type Checker)檢查和處理目標方法的局部變量和操做數棧所須要的類型是否匹配 |
Signature | 類、方法表、字段表 | JDK1.5中新增的屬性,這個屬性用於支持泛型狀況下的方法簽名,在Java語言中,任何類、接口、初始化方法或成員的泛型簽名若是包含了類型變量(Type Variables)或參數化類型(Parameterized Types),則Signature屬性會爲它記錄泛型簽名信息。因爲Java的泛型採用擦除法實現,在爲了不類型信息被擦除後致使簽名混亂,須要這個屬性記錄泛型中的相關信息。 |
SourceFile | 類文件 | 記錄源文件名稱 |
SourceDebugExtension | 類文件 | JDK1.6中新增的屬性,SourceDebugExtension屬性用於存儲額外的調試信息。譬如在進行JSP文件調試時,沒法經過Java堆棧來定位到JSP文件的行號,JSR-45規範爲這些非Java語言編寫,卻須要編譯成字節碼並運行在Java虛擬機中的程序提供了一個進行調試的標準機制,使用SourceDebugExtension屬性就能夠用於存儲這個標準所新加入的調試信息 |
Synthetic | 類、方法表、字段表 | 標識方法或字段爲編譯器自動生成的 |
LocalVariableTypeTable | 類 | JDK1.5中新增的屬性,它使用特徵簽名代替描述符,是爲了引入泛型語法以後能描述泛型參數化類型而添加 |
RuntimeVisableAnnotations | 類、方法表、字段表 | JDK1.5中新增的屬性,爲動態註解提供支持。RuntimeVisableAnnotations屬性用於指明哪些註解時運行時(實際上運行時就是進行反射調用)可見的。 |
RuntimeInvisableAnnotations | 類、方法表、字段表 | JDK1.5中新增的屬性,與RuntimeVisableAnnotations屬性做用恰好相反,用於指明哪些註解是運行時不可見的。 |
RuntimeVisableParameterAnnotations | 方法表 | JDK1.5中新增的屬性,做用與RuntimeVisableAnnotations相似,只不過做用對象爲方法參數 |
RuntimeInvisableParameterAnnotations | 方法表 | JDK1.5中新增的屬性,做用與RuntimeInvisableAnnotations屬性相似,只不過做用對象爲方法參數 |
AnnotationDefault | 方法表 | JDK1.5中的新增屬性,用於記錄註解類元素的默認值 |
BootstrapMethod | 類文件 | JDK1.7中新增的屬性,用於保存invokedynamic指令引用的引導方法限定符 |
對於每一個屬性,它的名稱須要從常量池中引用一個CONSTANT_Utf8_info類型的常量來表示而屬性值的結構則徹底是自定義的,只須要經過一個u4的長度屬性去說明屬性值所佔用的位數便可,一個符合規則的屬性表須要知足下表所定義的結構:code
屬性表結構對象
類型 | 名稱 | 數量 |
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u1 | info | attribute_length |
Code屬性blog
Class文件中屬性表中最重要的屬性類型,沒有之一。索引
Java程序方法體中的代碼通過javac編譯器處理後,最終變爲字節碼指令存儲在Code屬性內。Code屬性出如今方法表的屬性集合之中,但並非全部的方法表都必須存在這個屬性,譬如接口或者抽象類中的方法就不存在Code屬性,若是方法表有Code屬性存在,那麼它的結構以下表:接口
Code屬性表的結構
類型 | 名稱 | 數量 |
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_table_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
attribute_name_index是一項指向CONSTANT_Utf8_info型常量的索引,常量值固定爲"Code",它表明了該屬性的屬性名稱,attribute_length指示了屬性值的長度,因爲屬性名稱索引與屬性長度一共6字節,因此屬性值的長度固定爲整個屬性表長度減去6個字節。
max_stack表明了操做數棧(Operand Stacks)深度的最大值。在方法執行的任意時刻,操做數棧都不會超過這個深度。虛擬機運行的時候須要根據這個值來分配棧幀(Stack Frame)中的操做棧深度。
max_locals表明了局部變量表所需的存儲空間。在這裏,max_local的單位是Slot,Slot是虛擬機爲局部變量分配內存所使用的最小單位。對於byte、char、float、int、short、boolean和returnAddress等長度不超過32位的數據類型,每一個局部變量佔用1個Slot,而double和long這兩種64位的數據類型則須要兩個Slot來存放。方法參數(包括實例方法中的隱藏參數"this")、顯式異常處理器的參數(Exception Handler Paramter,就是try-catch語句中catch塊所定義的異常)、方法體中定義的局部變量都須要使用局部變量表來存放,另外,並非在方法中用到了多少個局部變量,就把這些局部變量所佔Slot之和座位max_locals的值,緣由是局部變量表中的Slot能夠重用,當代碼執行超出一個局部變量的做用域時,這個局部變量所佔用的Slot能夠被其它局部變量所使用,Javac編譯器會根據變量的做用域來分配Slot給各個變量使用,而後計算出max_locals的大小。
code_length和code用來存儲Java源程序編譯後生成的字節碼指令。code_length表明字節碼長度,code是用於存儲字節碼指令的一系列字節流。即然叫字節碼指令,那麼每一個指令就是一個u1類型的單字節,當虛擬機讀取到code中的一個字節碼時,就能夠對應找出這個字節碼的是什麼指令,而且能夠知道這條指令後面是否須要跟隨參數,以及參數應當如何理解。咱們知道一個u1數據類型的取值範圍爲Ox00~OxFF,對應十進制的0~255,也就是一共能夠表達256條指令。
Code屬性是Class文件中最重要的一個屬性,若是把一個Java程序中的信息分爲代碼(Code,方法體裏面的Java代碼)和元數據(Metadata,包括類、字段、方法定義以及其餘信息)兩部分,那麼在整個Class文件中,Code屬性用於描述代碼,全部其餘數據項目都用於描述元數據。
在字節碼指令以後的是這個方法的顯式異常處理表(下文簡稱異常表)集合,異常表對與Code屬性來講並非必須存在的。異常表的格式以下表所示,它包含4個字段,這些字段的含義爲:若是當字節碼在第start_pc行到end_pc行之間(不含第end_pc行)出現了類型爲catch_type或者其子類的異常(catch_type爲指向一個CONSTANT_Class_info型常量的索引),則轉到第handler_pc行繼續處理。當catch_type的值爲0時,表明任意異常都須要轉向到handler_pc處進行處理。
屬性表結構
類型 | 名稱 | 數量 |
u2 | start_pc | 1 |
u2 | end_pc | 1 |
u2 | handler_pc | 1 |
u2 | catch_type | 1 |
異常表其實是Java代碼的一部分,編譯器使用異常表而不是簡單的跳轉命令來實現Java異常激finally處理機制。