class字節碼,此次我算看透你了!

簡介

java代碼是經過java編譯器編譯成class文件,而後由jvm加載執行的,jvm屏蔽了底層平臺系統執行細節,因此能夠作到Compile Once,Run Anywhere。java

編譯後的class文件,是一個二進制流文件,例以下面的類:bootstrap

public class ServiceResult<T> {

    private static final int SUCCESS_CODE = 200;
    private static final String SUCCESS_MESSAGE = "Success";

    private int code;
    private String message;
    private T data;

    public ServiceResult(T data) {
        this.code = SUCCESS_CODE;
        this.message = SUCCESS_MESSAGE;
        this.data = data;
    }

    public ServiceResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public boolean isSuccess() {
        return code == SUCCESS_CODE;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public T getData() {
        return data;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("ServiceResult{");
        sb.append("code=").append(code);
        sb.append(", message='").append(message).append('\'');
        sb.append(", data=").append(data);
        sb.append('}');
        return sb.toString();
    }
}
複製代碼

編譯後獲得的class,以16進制格式打開以下:數組

注:class文件以字節(8比特)爲單位,用u1,u2,u4,u8分別表示1個字節,2個字節,4個字節,8個字節的無符號數,採用Big-edian形式,即高位字節在前。微信

Class結構

二進制class文件若是用類c語言結構體的形式來描述其邏輯結構,則以下圖所示:app

從圖中可知,class文件主要包含magic,minor version,major version,constant pool,access flags,this_class,super class,interfaces,fields,methods,attributes 11個部分,每一個部分之間緊湊的拼接在一塊兒,沒有分界符分割,下面分別介紹每一個結構。jvm

在開始介紹各個結構以前,須要說明本文以jvm1.8爲準ide

本文有些長,這裏排版看起來更舒服些工具

1. magic

魔數: 佔4個字節的無符號數,固定爲0xCAFEBABE,用來標識改文件是一個class文件ui

2. minor version

次版本號: 佔兩個字節的無符號數,範圍0~65535,與major version一塊兒表示當前class文件的版本,jvm能夠向前兼容以前的版本,但不能向後兼容,即jdk7的虛擬機不能運行jdk8編譯的classthis

3. major version

主版本號: 佔兩個字節的無符號數,jdk1.1使用的主版本號是45,之後每一個大版本加1,如jdk1.8爲52

4. constant pool

常量池: 常量池是class中十分重要的一部分,它可不是隻保存着類中定義的常量而已,還保存着class文件中的各類元數據,包括一些字符串,類名,接口名,字段名,方法名等等……,它的做用就是被引用,常量池部分首先有兩個字節u2記錄它包含的常量個數。

PS1:常量池就是一系列常量的數組,它的下標是從1開始的,即有效大小是constant_pool_count-1,第0項是無效的,有些結構能夠用索引0來表示沒有對常量的引用

PS2:常量池的設計有效的減少的class文件的大小,想一想那些重複使用的類名稱,字符串如今只需保留一份,而且引用的地方只須要用u2保存它在常量池中的索引就能夠了

​ 由於每一個常量都有一種具體的類型來表明不一樣的含義,光知道常量的個數還沒辦法解析出具體的常量項來,因此定義每一個常量的第一個字節u1表示該常量的類型tag,而後就能夠根據該類型常量的存儲結構來解析了。

​ 常量的tag有CONSTANT_Utf8,CONSTANT_Integer,CONSTANT_Float,CONSTANT_Long,CONSTANT_Double,CONSTANT_Class,CONSTANT_String,CONSTANT_Fieldref,CONSTANT_Methodref,CONSTANT_InterfaceMethodref,CONSTANT_NameAndType,CONSTANT_MethodHandle,CONSTANT_MethodType,CONSTANT_InvokeDynamic等14種,下面對每種類型結構(類型+「_info」)做下介紹:

4.1 CONSTANT_Utf8_info

常量池中最基本的常量,用來保存一個utf8編碼字符串,如常量字符串,類名,字段名,方法名等的值都是一個對它的引用(索引)

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}
複製代碼

tag=1,length表示字符串字節長度,如length=20,則表示接下來20個bytes是一個utf8編碼的字符串。

這裏補充兩點:

  • java使用的是可變utf8編碼:ASCII 字符('\u0001' ~ '\u007F',即1~127)用1個字節表示,null('\u0000')和 '\u0080' 到 '\u07FF'之間的字符用2個字節表示, '\u0800' 到 '\uFFFF'之間的字符用3個字節表示。

    逆向來看就是若是讀到一個字節最高位是0,則是一個單字節字符。

    讀到一個字節最高3位是110則是一個雙字節字符,緊接着還要再讀1個字節。

    讀到一個字節最高4位是1110,則是一個三字節字符,緊接着還要再讀2個字節。

    關於如何解碼能夠查看官方文檔,在java中,咱們只須要使用new String(bytes, StandardCharset.UTF8)便可獲得解碼字符串

  • length使用了u2(0-65535)來表示,則其表示的字符串最大長度爲65535

4.2 CONSTANT_Integer_info
CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}
複製代碼

int,tag=3,接下來4個字節表示該int的值。關於CONSTANT_Integer補充如下幾點:

  • big-endian,字節高位在前,下文同理

    若是本身解析則要像下面這樣:

    int value = 0;
    byte[] data = new byte[4];
    is.read(data);
    value = (value | (((int) data[0]) & 0xff)) << Byte.SIZE * 3;
    value = (value | (((int) data[1]) & 0xff)) << Byte.SIZE * 2;
    value = (value | (((int) data[2]) & 0xff)) << Byte.SIZE;
    value = (value | (((int) data[3]) & 0xff));
    複製代碼

    咱們可使用DataInputStream的readInt()方法讀取一個int值。

  • java中short, char, byte, boolean使用int來表示,boolean數組則用byte數組來表示(1個byte表示1個boolean元素)

4.3 CONSTANT_Float_info
CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}
複製代碼

float浮點數,tag=4,接下來4個字節表示它的值,採用 IEEE 754標準定義。可使用DataInputStream的readFloat()方法讀取一個float值。

4.4 CONSTANT_Long_info
CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
複製代碼

tag=5,長整數,long和double在class中用兩個部分(高位4字節,地位4字節)保存。可使用DataInputStream的readLong()方法讀取一個float值。

4.5 CONSTANT_Double_info
CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
複製代碼

tag=6,雙精度浮點數,採用 IEEE 754標準定義。存儲同CONSTANT_Long同樣。

4.6 CONSTANT_Class_info
CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}
複製代碼

tag=7,表示一個類或接口,注意不是field的類型或method的參數類型、返回值類型。name_index是常量池索引,該索引處常量確定是一個CONSTANT_Utf8_info

4.7 CONSTANT_String_info
CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}
複製代碼

tag=8,表示一個常量字符串,string_index是常量池索引,該索引處常量確定是一個CONSTANT_Utf8_info ,存儲着該字符串的值

4.8 CONSTANT_Fieldref_info
CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
複製代碼

tag=9,表示一個引用field信息,包括靜態field和實例field。

class_index是常量池中一個CONSTANT_Class_info類型常量(類/接口)索引,表示field所屬類。name_and_type_index是常量池中一個CONSTANT_NameAndType_info(見下文)類型常量索引,表示field的名稱和類型。

關於field引用解釋一下,包括下面的method,接口method引用同理:

  • 以本文開頭ServiceResult類的code field爲例,code在多個方法中都有用到,相比保存多份該field信息來說,在常量池中保存一份該field信息,而後在其餘用到的地方保存其索引顯然更合適。
  • CONSTANT_Fieldref_info是把在代碼中引用的field(多是本類的,也多是外部類的)抽離成常量,與後面講的field_info不要混淆
4.9 CONSTANT_Methodref_info
CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
複製代碼

tag=10,表示一個引用method信息,包括靜態method和實例method。

class_index是常量池中一個CONSTANT_Class_info類型常量(這裏只能是類)索引,表示method所屬類。name_and_type_index是常量池中一個CONSTANT_NameAndType_info類型常量索引,表示method的名稱和參數,返回值信息。

4.10 CONSTANT_InterfaceMethodref_info
CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
複製代碼

tag=11,表示一個接口method信息。

class_index是常量池中一個CONSTANT_Class_info類型常量(這裏只能是接口)索引,表示method所屬接口。name_and_type_index同CONSTANT_Methodref_info。

4.11 CONSTANT_NameAndType_info
CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}
複製代碼

tag=12,存儲field或method的名稱,類型等信息,能夠看出它又是兩個引用。name_index指向一個CONSTANT_Utf8_info,表示字段或方法的非全限定名稱。descriptor_index也指向一個CONSTANT_Utf8_info,表示該字段/方法的描述信息。

Descriptor

descriptor用一個字符串CONSTANT_Utf8_info保存。

  • 字段描述符(FieldType),FieldType能夠是基本類型:B(byte) C(char) D(double) F(float) I(int) J(long) S(short) Z(boolean),對象類型:L+全限定類名,數組類型:[+元素類型

    int a; // I
    Integer b; //Ljava/lang/Integer
    double[] c; //[D
    double[][] d; //[[D
    Object[] e; //[Ljava/lang/Object
    Object[][][] f; //[[[Ljava/lang/Object
    複製代碼
  • 方法描述符(MethodDescriptor),MethodDescriptor格式爲(參數類型)返回類型

    /** * 描述符:(IDLjava/lang/Thread;)Ljava/lang/Object; */
    Object m(int i, double d, Thread t) {...}
    複製代碼
4.12 CONSTANT_MethodHandle_info
CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}
複製代碼

tag=15,方法句柄,好比獲取一個類靜態字段,實例字段,調用一個方法,構造器等都會轉化成一個句柄引用。

  • reference_kind

    Kind Description Interpretation
    1 REF_getField getfield C.f:T
    2 REF_getStatic getstatic C.f:T
    3 REF_putField putfield C.f:T
    4 REF_putStatic putstatic C.f:T
    5 REF_invokeVirtual invokevirtual C.m:(A*)T
    6 REF_invokeStatic invokestatic C.m:(A*)T
    7 REF_invokeSpecial invokespecial C.m:(A*)T
    8 REF_newInvokeSpecial new C; dup; invokespecial C.<init>:(A*)V
    9 REF_invokeInterface invokeinterface C.m:(A*)T

    f: field,m: method,:實例構造器

  • reference_index

    • 對於Kind=1,2,3,4,reference_index引用一個CONSTANT_Fieldref_info
    • 對於Kind=5,6,7,8,reference_index引用一個CONSTANT_Methodref_info
    • 對於Kind=9,reference_index引用一個CONSTANT_InterfaceMethodref_info
4.13 CONSTANT_MethodType_info
CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}

複製代碼

tag=16,描述一個方法類型。descriptor_index引用一個CONSTANT_Utf8_info,表示方法的描述符

4.14 CONSTANT_InvokeDynamic_info
CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

複製代碼

tag=18,invokedynamic動態調用指令引用信息。

  • bootstrap_method_attr_index,BootstrapMethods屬性中bootstrap_methods[]數組的索引,每一個引導方法引用了CONSTANT_MethodHandle_info
  • name_and_type_index,引用一個CONSTANT_NameAndType_info常量
5. access flags

access flags表示類,接口,字段,方法的訪問控制和修飾信息。

Access Flag(u2) Value 做用對象
ACC_PUBLIC 0x0001 class, inner, field, method
ACC_PRIVATE 0x0002 inner, field, method
ACC_PROTECTED 0x0004 inner, field, method
ACC_STATIC 0x0008 inner, field, method
ACC_FINAL 0x0010 class, inner, field, method
ACC_SUPER 0x0020 class
ACC_SYNCHRONIZED 0x0020 method
ACC_VOLATILE 0x0040 field
ACC_BRIDGE 0x0040 method
ACC_TRANSIENT 0x0080 field
ACC_VARARGS 0x0080 method
ACC_NATIVE 0x0100 method
ACC_INTERFACE 0x0200 class, inner
ACC_ABSTRACT 0x0400 class, inner, method
ACC_STRICT 0x0800 method
ACC_SYNTHETIC 0x1000 class, inner, field, method
ACC_ANNOTATION 0x2000 class, inner
ACC_ENUM 0x4000 class, inner, field

其中大部分都能見名知意,補充如下幾點:

  • ACC_SUPER:用於invokespecial指令而須要特殊處理的父類方法
  • ACC_BRIDGE:橋方法標誌,有該標誌的方法上同時有ACC_SYNTHETIC標誌
  • ACC_STRICT:strictfp,strict float point,方法使用 FP-strict 浮點格式
  • ACC_SYNTHETIC:標誌是由編譯器生成的,源碼中並無
6. this class

當前類或接口,指向一個CONSTANT_Class_info常量,能夠從中解析當前類的全限定名稱。包名層次用/分割,而不是.,如java/lang/Object

7. super class

當前類的直接父類索引,指向一個CONSTANT_Class_info常量,當沒有直接父類時super_class=0

8. interfaces

首先用u2代表當前類或接口的直接父接口數量n。緊接着n個u2組成的數組便是這些父接口在常量池的索引,類型是CONSTANT_Class_info,按聲明順序從左至右。

9. fields
field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

複製代碼

field_info保存當前類的fields信息。很簡單,其中大部分前面都講過了,關於attributes放在下文第11節專門講解。須要注意的是fields只包含當前類的字段,如A的內部類B的字段c,則是在類A$B中

10. methods
method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

複製代碼

保存當前類的方法信息,同field_info

11. attributes

屬性表:屬性存在與ClassFile, field_info, method_info中,此外Code屬性中又包含嵌套屬性信息,屬性用來描述指令碼,異常,註解,泛型等信息,JLS8預約義了23種屬性,每種屬性結構不一樣(變長),但能夠抽象成下面通用結構。

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

複製代碼

attribute_name_index:是該屬性名稱在常量池中的索引,經過該名稱才能夠斷定當前屬性屬於具體哪種,如「Code」表示當前是一個Code_attribute

attribute_length:表示接下來多少字節是該屬性的內容信息,java容許自定義新的屬性,若是jvm不認識,則按通用結構直接讀取attribute_length個字節。

23種屬性按做用能夠分爲3組:

  • 被jvm翻譯使用:ConstantValue,Code,StackMapTable,Exceptions,BootstrapMethods
  • 被java類庫解析使用:InnerClasses,EnclosingMethod,Synthetic,Signature,RuntimeVisibleAnnotations/RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations/RuntimeInvisibleParameterAnnotations,RuntimeVisibleTypeAnnotations/RuntimeInvisibleTypeAnnotations,AnnotationDefault,MethodParameters
  • 既不要求jvm解析,也不要求java類庫解析,用於調試工具等場景:SourceFile,SourceDebugExtension,LineNumberTable,LocalVariableTable,LocalVariableTypeTable,Deprecated

注:後面我會介紹如何解析class,因此本文只對每一個屬性的結構和做用作一個簡單介紹

11.1 ConstantValue
ConstantValue_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 constantvalue_index;
}

複製代碼

存在於field_info,表明一個常量值,如private final int x = 5中的5。attribute_name_index引用的值是「ConstantValue」,attribute_length固定爲2,接下來兩個字節的constantvalue_index是該常量值在常量池中的索引,是CONSTANT_Long,CONSTANT_Float,CONSTANT_Double,CONSTANT_Integer,CONSTANT_String的一種。

11.2 Code
Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

複製代碼

描述方法體編譯後的字節碼指令。前面講過描述方法的method_info結構,而方法的方法體信息就存在它的屬性表中code屬性內。若是是抽象方法,那就沒有這個屬性。

前面在講屬性通用結構attribute_info的時候已經講過attribute_name_index, attribute_length,它是每一個屬性都有的,下文就不在說明了,只對其餘部分介紹。

  • max_stack , 操做數棧的最大深度,用來分配棧的大小

  • max_locals, 方法棧幀中局部變量表最大容量,存儲局部變量,方法參數,異常參數等。以slot爲單位,32bit之內的變量用分配1個slot,大於32bit,如long、double分配2個slot,注意對象存的是引用。另外指出一點,對於實例方法,默認會傳入this對象指針,因此這時的max_locals最小爲1。

  • code[code_length],存儲字節碼指令列表,每條字節碼指令是一個byte,這樣8bit最多能夠表示256條不一樣指令,須要指出的是這個字節流數組存的不全是指令,有的指令還有對應的操做數,跳過相應n個字節的操做數再日後纔是下一條指令,詳細內容我會在另外的文章中演示。

  • exception_table[exception_table_length],方法異常表,注意不是方法聲明拋出的異常,而是顯示try-catch的異常,每一個catch的異常時exception_table的一項。

    • catch_type,捕獲的異常類型,指向一個CONSTANT_Class_info常量
    • start_pc,字節碼指令相對方法開始的偏移量,至關於code[code_length]中的索引
    • end_pc, 字節碼指令相對方法開始的偏移量,至關於code[code_length]中的索引
    • handler_pc,字節碼指令相對方法開始的偏移量,至關於code[code_length]中的索引

    這幾項表示的意思是:若是在[start_pc, end_pc)區間發生了catch_type類型或其子類的異常(catch_type=0表示捕獲任意異常),則跳轉至handler_pc處的指令繼續執行。

    補充三點:

    1)關於finaly塊中的指令採用的方式是在每一個代碼分支中冗餘一份。

    2)關於未顯示捕獲的異常則經過athrow指令繼續拋出

    3)雖然指令長度code_length是u4,但start_pc,end_pc,handler_pc都只有2個字節的無符號數u2,最大表示範圍只有65535,所以方法最多隻能有65535條指令(每條指令都不帶操做數的狀況下)

  • attributes[attributes_count],嵌套屬性列表

11.3 StackMapTable
StackMapTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_entries;
    stack_map_frame entries[number_of_entries];
}

複製代碼

上面講到Code_attribute中也能夠包含屬性表,StackMapTable就位於Code屬性的屬性表中,它是爲了在jvm字節碼驗證階段作類型推導驗證而添加的

11.4 Exceptions
Exceptions_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_exceptions;
    u2 exception_index_table[number_of_exceptions];
}

複製代碼

表示經過throws聲明的可能拋出的異常,結構很簡單exception_index_table每一項u2指向一個CONSTANT_Class_info常量

11.5 BootstrapMethods
BootstrapMethods_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 num_bootstrap_methods;
    {   u2 bootstrap_method_ref;
        u2 num_bootstrap_arguments;
        u2 bootstrap_arguments[num_bootstrap_arguments];
    } bootstrap_methods[num_bootstrap_methods];
}

複製代碼

位於ClassFile中,保存 invokedynamic 指令引用的引導方法

  • bootstrap_method_ref,引用一個一個 CONSTANT_MethodHandle_info 常量,此時該MethodHandle的reference_kind 一定爲REF_invokeStatic或REF_newInvokeSpecial
  • num_bootstrap_arguments,引導方法參數列表,數組中每一項是一個 CONSTANT_String_info, CONSTANT_Class_info, CONSTANT_Integer_info, CONSTANT_Long_info, CONSTANT_Float_info, CONSTANT_Double_info, CONSTANT_MethodHandle_info, or CONSTANT_MethodType_info引用
11.6 InnerClasses
InnerClasses_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    {   u2 inner_class_info_index;
        u2 outer_class_info_index;
        u2 inner_name_index;
        u2 inner_class_access_flags;
    } classes[number_of_classes];
}
複製代碼

記錄內部類信息,classes就是當前類的內部類列表,其中inner_class_info_index,outer_class_info_index指向CONSTANT_Class型常量,分別表明內部類和外部類信息引用,inner_name_index是內部類名稱的引用(CONSTANT_Utf8_info),等於0則表明是匿名內部類,inner_class_access_flags是內部類訪問標誌,同access_flags

11.7 EnclosingMethod
EnclosingMethod_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 class_index;
    u2 method_index;
}
複製代碼

位於ClassFile結構中,存儲局部類或匿名類信息。

  • class_index,對直接包含它的類的引用,引用一個CONSTANT_Class_info常量,表明包含當前類聲明的最內層類
  • method_index,引用一個CONSTANT_NameAndType_info常量,表示直接包含該局部類、匿名類的方法名稱和類型
11.8 Synthetic
Synthetic_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

複製代碼

標記是否類、方法、字段爲編譯器生成,與ACC_SYNTHETIC同義,attribute_length=0,存在該屬性則表示true。

11.9 Signature
Signature_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 signature_index;
}

複製代碼

存在於類,方法,字段的屬性表中,用於存儲類,方法,字段的泛型信息(類型變量Type Variables,參數化類型Parameterized Types)。

關於泛型能夠參考這裏

  • signature_index,引用一個CONSTANT_Utf8_info常量,表示簽名
11.10 RuntimeVisibleAnnotations
RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

複製代碼

存在於類,方法,字段,存儲運行時可見的(RetentionPolicy.RUNTIME)註解信息,能夠被反射API獲取到,關於註解能夠參考這裏

annotation結構存儲了註解名稱,元素值對的信息,具體能夠參考官方文檔,或者我後面class解析的文章

11.11 RuntimeInvisibleAnnotations
RuntimeInvisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

複製代碼

與RuntimeVisibleAnnotations結構相同,但不可見,即不能被反射API獲取到,目前jvm忽略此屬性

11.12 RuntimeVisibleParameterAnnotations
RuntimeVisibleParameterAnnotations_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 num_parameters;
    {   u2         num_annotations;
        annotation annotations[num_annotations];
    } parameter_annotations[num_parameters];
}

複製代碼

存在於method_info的屬性表中,存儲運行時可見的方法參數註解信息,與RuntimeVisibleAnnotations對比發現,RuntimeVisibleParameterAnnotations存儲的是方法的參數列表上每一個參數的註解(至關與一組RuntimeVisibleParameterAnnotations),順序與方法描述符中參數順序一致

11.13 RuntimeInvisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 num_parameters;
    {   u2         num_annotations;
        annotation annotations[num_annotations];
    } parameter_annotations[num_parameters];
}
複製代碼

不想再囉嗦了

11.14 RuntimeVisibleTypeAnnotations
RuntimeVisibleTypeAnnotations_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              num_annotations;
    type_annotation annotations[num_annotations];
}
複製代碼

存在於class_file,method_info,field_info,code的屬性表中,java8新增。JLS8新增兩種ElementType(ElementType.TYPE_PARAMETER, ElementType.TYPE_USE),相應用來描述的註解屬性也作了相應的改的,就有了該屬性,type_annotation存儲着註解信息及其做用對象。

11.15 RuntimeInvisibleTypeAnnotations
RuntimeInvisibleTypeAnnotations_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              num_annotations;
    type_annotation annotations[num_annotations];
}
複製代碼

略。。。

11.16 AnnotationDefault
AnnotationDefault_attribute {
    u2            attribute_name_index;
    u4            attribute_length;
    element_value default_value;
}

複製代碼

存在於method_info屬性表 ,記錄註解元素的默認值

11.17 MethodParameters
MethodParameters_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 parameters_count;
    {   u2 name_index;
        u2 access_flags;
    } parameters[parameters_count];
}

複製代碼

存在於method_info屬性表 ,記錄方法參數信息,name_index形參名稱,access_flags有ACC_FINAL,ACC_SYNTHETIC,ACC_MANDATED

11.18 SourceFile
SourceFile_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 sourcefile_index;
}

複製代碼

class_file屬性表中,記錄生成該的文件名,異常堆棧可能顯示此信息,通常與類名相同,但內部類不是。這是一個可選屬性,意味着不強制編譯器生成此信息。

11.19 SourceDebugExtension
SourceDebugExtension_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 debug_extension[attribute_length];
}
複製代碼

存在於class結構中,可選,保存非java語言的擴展調試信息。debug_extension 數組是指向CONSTAN_Utf8_info的索引

11.20 LineNumberTable
LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;	
    } line_number_table[line_number_table_length];
}
複製代碼

code的屬性表中,存儲源碼行號與字節碼偏移量(方法第幾條指令)之間映射關係,start_pc字節碼偏移量,line_number源碼行號,可選。

問題:在錯誤堆棧中如何打印出出錯的源碼行號的?如何支持在源碼上斷點調試?

11.21 LocalVariableTable
LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}
複製代碼

code的屬性表中,存儲棧幀中局部變量表的變量與源碼中定義的變量的映射,能夠在解析code屬性時關聯到局部變量表變量在源碼中的變量名等,可選。

11.22 LocalVariableTypeTable
LocalVariableTypeTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_type_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 signature_index;
        u2 index;
    } local_variable_type_table[local_variable_type_table_length];
}
複製代碼

code的屬性表中,與LocalVariableTable類似,signature_index也引用一個CONSTANT_Utf8_info 常量,對應含有泛型的變量會同時存儲到LocalVariableTable和LocalVariableTypeTable中個一份

11.23 Deprecated
Deprecated_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}
複製代碼

類、方法、字段過時標記,沒有額外信息,attribute_length=0,若是出現該屬性則說明加了@deprecated註解

完!若是以爲寫的還能夠,給個贊鼓勵一下吧!


下期預告:動手編寫一個解析class(字節碼)文件的程序

關注微信號,更多精彩等着你

相關文章
相關標籤/搜索