JVM003_属性表
屬性表
預備知識
- javac -g Xxx.java 在生成class文件的時候生成所有調試信息
- javap -v Xxx.class 輸出附加信息
屬性表結構
| u2 | attribute_name_index | 1 | 屬性名稱索引,指向一個CONSTANT_Utf8_info型常量的索引 |
| u4 | attribute_length | 1 | 該屬性表的長度 |
| u1 | info | attribute_length | 屬性值 |
Code屬性表
Code屬性表結構釋義
-
attribute_name_index是一項指向CONSTANT_Utf8_info型常量的索引,對應的值固定為‘Code’,代表了該屬性的屬性名稱。
-
attribute_length代表了屬性值的長度。屬性值的長度=屬性表總長度-6個字節。
-
max_stack代表操作數棧深度的最大值。虛擬機運行時根據該值來分配棧幀中操作數棧的深度。
-
max_locals 代表了局部變量表所需的存儲空間。其計算單位為‘變量槽’。變量槽是JVM為局部變量表分配內存所使用的最小單位。對于byte、char、float、int、short、boolean及returnAddress等長度不超過32位的數據類型,每個局部變量占用1個變量槽,double及long這兩種64位的數據類型,占用兩個變量槽。
- 局部變量表中的變量槽會被復用,所以變量槽的個數不一定等于參數個數。當代碼執行 超過一個變量的作用域時,其原型占用的變量槽將會被復用。
- 操作數棧及局部變量表直接決定了該方法的棧幀所耗費的內存。
-
code_length代表Java源程序編譯后生成的字節碼指令個數,由于一個字節碼占用1字節,所以也就是字節碼的長度。
- 理論值為2^32,實際最大值為65535。
-
code 用于存儲編譯生產的一些列字節碼指令。
-
exception_info 該方法的顯示異常處理表集合,非必須。
-
顯示異常處理表結構
類型名稱數量 u2 start_pc 1 u2 end_pc 1 u2 handler_pc 1 u2 catch_type 1 上表表示當字節碼從[start_pc,end_pc)行出現了類型為catch_type或其子類的異常,那么就轉至handler_pc行進行處理。當catch_type為0時,代表任意異常都需要跳轉到handler_pc行處理。
-注意 : 此處的行指的是字節碼相對于方法體開始的偏移量。
-
Code屬性表實例
public class Test{private static final int size = 100;public void sout(){System.out.println(size);}public int toDouble(int prame){return 2*prame;}public int add(int p1,int p2){return p1+p2;}public static int toTriple(int prame){return 3*prame;} }將代碼編譯 javac -g Test.java,在反編譯javap -v Test.class
Classfile /D:/notes/JVM/code/Test.classLast modified 2021-3-29; size 746 bytesMD5 checksum a1a23cf998350d444d9438782f3af659Compiled from "Test.java" public class Testminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER Constant pool:#1 = Methodref #5.#28 // java/lang/Object."<init>":()V#2 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;#3 = Class #31 // Test#4 = Methodref #32.#33 // java/io/PrintStream.println:(I)V#5 = Class #34 // java/lang/Object#6 = Utf8 size#7 = Utf8 I#8 = Utf8 ConstantValue#9 = Integer 100#10 = Utf8 <init>#11 = Utf8 ()V#12 = Utf8 Code#13 = Utf8 LineNumberTable#14 = Utf8 LocalVariableTable#15 = Utf8 this#16 = Utf8 LTest;#17 = Utf8 sout#18 = Utf8 toDouble#19 = Utf8 (I)I#20 = Utf8 prame#21 = Utf8 add#22 = Utf8 (II)I#23 = Utf8 p1#24 = Utf8 p2#25 = Utf8 toTriple#26 = Utf8 SourceFile#27 = Utf8 Test.java#28 = NameAndType #10:#11 // "<init>":()V#29 = Class #35 // java/lang/System#30 = NameAndType #36:#37 // out:Ljava/io/PrintStream;#31 = Utf8 Test#32 = Class #38 // java/io/PrintStream#33 = NameAndType #39:#40 // println:(I)V#34 = Utf8 java/lang/Object#35 = Utf8 java/lang/System#36 = Utf8 out#37 = Utf8 Ljava/io/PrintStream;#38 = Utf8 java/io/PrintStream#39 = Utf8 println#40 = Utf8 (I)V {public Test();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this LTest; // 疑問:為什么這里的Length為5?public void sout();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: bipush 1005: invokevirtual #4 // Method java/io/PrintStream.println:(I)V8: returnLineNumberTable:line 5: 0line 6: 8LocalVariableTable:Start Length Slot Name Signature0 9 0 this LTest;public int toDouble(int);descriptor: (I)Iflags: ACC_PUBLICCode:stack=2, locals=2, args_size=20: iconst_21: iload_12: imul3: ireturnLineNumberTable:line 9: 0LocalVariableTable:Start Length Slot Name Signature0 4 0 this LTest;0 4 1 prame Ipublic int add(int, int);descriptor: (II)Iflags: ACC_PUBLICCode:stack=2, locals=3, args_size=30: iload_11: iload_22: iadd3: ireturnLineNumberTable:line 13: 0LocalVariableTable:Start Length Slot Name Signature0 4 0 this LTest;0 4 1 p1 I0 4 2 p2 Ipublic static int toTriple(int);descriptor: (I)Iflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: iconst_31: iload_02: imul3: ireturnLineNumberTable:line 17: 0LocalVariableTable:Start Length Slot Name Signature0 4 0 prame I }現在對toDouble方法進行分析
Code屬性下
- stack=2表示操作數棧的最大深度為2
- locals=2表示本地變量表的存儲空間為2個變量槽
- args_size=2表示方法的傳參有2個
- 這里有個問題,明明方法簽名為toDouble(int prame),只有一個參數,為什么這里顯示有兩個參數呢?因為在編譯的時候,會自動的傳入一個參數this。并放到局部變量表的0號槽中。
- LocalVariableTable 表示各變量在局部變量表中的存儲情況。
Exceptions屬性
Exceptions屬性是在方法表中與Code屬性平級的一項屬性,與Code屬性中的異常表不是同一東西,列舉出方法中可能拋出的受查異常(Checked Excepitons) , 也就是方法描述時在throws關鍵字后面列舉的異常。
Exceptions屬性結構
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u2 | number_of_exceptions | 1 | 拋出的受查異常種數 |
| u2 | exception_index_table | nubmer_of_exceptions | 指向常量池中CONSTANT_class_info型常量的索引 |
LineNumberTable屬性
用來描述Java源碼與字節碼行號之間的對應關系。這里的字節碼行號指的是字節碼距方法體開始的偏移量。是一個非必須屬性。默認生成。可使用-g: none或-g:lines來選擇或者取消。
若不生成改屬性,那么在拋出異常的時候,堆棧中將不會顯示出錯的行號,在調試時無法按照源碼行來設置斷點。
LineNumberTable屬性結構
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u2 | line_number_table_length | 1 | 屬性個數 |
| line_number_info | line_number_table | line_number_table_length | line_number_info集合 |
line_number_info
| u2 | start_pc | 字節碼行號 |
| u2 | line_number | Java源碼行號 |
LocalVariableTable
用來描述棧幀中局部變量表的變量與Java源碼中定義的變量之間的關系,是非必須的。-g:none,-g: vars來取消或選擇。默認會生成到class文件中,若取消該屬性,那么當他人引用時,所有的參數名稱都會消失。
LocalVariableTable屬性結構
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u2 | local_variable_table_length | 1 | 本地變量表長度 |
| local_variable_info | local_variable_table | local_variable_table_length | 本地變量表 |
local_variable_info結構
| u2 | start_pc | 1 | 該局部變量的聲明周期的開始字節碼偏移量 |
| u2 | length | 1 | 該局部變量的作用范圍 |
| u2 | name_index | 1 | 是指向CONSTANT_Utf8_info型常量的索引,代表局部變量的名稱 |
| u2 | descriptor_index | 1 | 是指向CONSTANT_Utf8_info型常量的索引,代表局部變量的描述符 |
| u2 | index | 1 | 在局部變量表中的變量槽位置 |
在JDK引入泛型后增加了**LocalVariableTypeTable **屬性,僅僅是將descriptor_index替換為字段的特征簽名(Signature) 。
SourceFile及SourceDebugExtension屬性
SourceFile屬性用于記錄生成這個Class文件的源碼文件名稱。 可選;-g:none,-g: source來關閉或開啟。若不生成,在拋出異常的時候,堆棧中不會顯示出錯代碼所屬的文件名。
SouceFile屬性結構
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u2 | sourcefile_index | 1 | 指向常量池中CONSTANT_Utf8_info型常量的索引, 常量值是源碼文件的文件名 |
JDK 5時, 新增了SourceDebugExtension屬性用于存儲額外的代碼調試信息。
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u1 | debug_extension[attribute_length] | 1 | 額外的調試信息 |
ConstantValue屬性
該屬性的作用是通知虛擬機自動為靜態變量賦值。只有被static修飾的變量才能使用這項屬性。
| 靜態變量 | 類構造器()方法中賦值 |
| 靜態變量 | 使用ConstantValue屬性 |
Oracle公司的選擇是,常量(static final共同修飾的變量)且其類型為基礎類型或String,使用ConstantValue屬性來進行初始化,非基礎類型及字符串,或僅被static修飾那么在()中進行初始化。
ConstantValue屬性結構
| u2 | attribute_name_index | 1 | 屬性名索引 |
| u4 | attribute_length | 1 | 屬性長度 |
| u2 | constantvalue_index | 1 | 常量池中一個字面量的引用 |
總結
以上是生活随笔為你收集整理的JVM003_属性表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL高级 —— 高性能索引
- 下一篇: Maven学习(五)————依赖的特性辨