[JVM]理解Class文件(3)

1. 引言

在上兩篇理解Class文件(1):手動(dòng)解析常量池[JVM]理解Class文件(2)中,已經(jīng)對(duì)class文件中的大部分內(nèi)容做了解析,接下來再看下class文件中剩下的最后兩個(gè)字段:methods和attributes

  • ClassFile結(jié)構(gòu)

ClassFile {
    u4 magic; 
    u2 minor_version; 
    u2 major_version; 
    u2 constant_pool_count; 
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags; 
    u2 this_class; 
    u2 super_class; 
    u2 interfaces_count; 
    u2 interfaces[interfaces_count]; 
    u2 fields_count; 
    field_info fields[fields_count]; 
    u2 methods_count; 
    method_info methods[methods_count]; 
    u2 attributes_count; 
    attribute_info attributes[attributes_count]; 
}
  • 示例TestClass文件

將生成的class文件用UltraEdit打開,可以清楚地看到Java編譯后生成的字節(jié)碼,我們要解析的內(nèi)容也就是這些字節(jié)碼。

00000000h: CA FE BA BE 00 00 00 33 00 12 0A 00 03 00 0E 07 ; 
00000010h: 00 0F 07 00 10 01 00 04 54 45 53 54 01 00 12 4C ; 
00000020h: 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 ; 
00000030h: 3B 01 00 0D 43 6F 6E 73 74 61 6E 74 56 61 6C 75 ; 
00000040h: 65 08 00 11 01 00 06 3C 69 6E 69 74 3E 01 00 03 ; 
00000050h: 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E ; 
00000060h: 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 0A 53 ;
00000070h: 6F 75 72 63 65 46 69 6C 65 01 00 0E 54 65 73 74 ;
00000080h: 43 6C 61 73 73 2E 6A 61 76 61 0C 00 08 00 09 01 ; 
00000090h: 00 09 54 65 73 74 43 6C 61 73 73 01 00 10 6A 61 ; 
000000a0h: 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 ;  
000000b0h: 0B 74 65 73 74 20 73 74 72 69 6E 67 00 21 00 02 ;  
000000c0h: 00 03 00 00 00 01 00 1A 00 04 00 05 00 01 00 06 ;  
000000d0h: 00 00 00 02 00 07 00 01 00 01 00 08 00 09 00 01 ;  
000000e0h: 00 0A 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 ; 
000000f0h: 00 01 B1 00 00 00 01 00 0B 00 00 00 06 00 01 00 ; 
00000100h: 00 00 02 00 01 00 0C 00 00 00 02 00 0D          ; 
  • TestClass常量池

TestClass常量池

2. methods_count、methods

  • ** 2.1 method_info**

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

雖然methods字段結(jié)構(gòu)體看起來很簡單,但是由于attribute_info中又嵌套了其他屬性,導(dǎo)致這部分的解析過程會(huì)比想象中要復(fù)雜。先附上TestClass的methods解析示例圖,然后再來看各個(gè)字段代表的含義。


methods字段解析
  • ** 2.2 access_flags**

access_flags定義當(dāng)前方法的訪問權(quán)限和基本屬性的掩碼標(biāo)志,它的取值范圍和相應(yīng)含義如下圖:


方法 access_flags標(biāo)記列表

截止到attribute_name_index這個(gè)字段,前面部分都比較簡單,對(duì)應(yīng)字段含義如下:

Option Values Description
methods_count 0x00 01 方法個(gè)數(shù)為1
access_flags 0x00 01 ACC_PUBLIC
name_index 0x00 08 對(duì)應(yīng)常量池索引為8的常量,即<init>
descriptor_index 0x00 09 對(duì)應(yīng)常量池索引為9,即()V
attributes_count 0x00 01 屬性個(gè)數(shù)為1
attribute_name_index 0x00 0A Code
  • 2.3 code

接下來,來看第一個(gè)表結(jié)構(gòu)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];
}
Option Values Description
attribute_name_index 0x00 0A Code
attribute_length 0x00 00 00 0D 當(dāng)前屬性的長度,不包括開始的6個(gè)字節(jié)(即不包括attribute_name_index和attribute_length的長度)
max_stack 0x00 01 當(dāng)前方法的操作數(shù)棧在運(yùn)行執(zhí)行的任何時(shí)間點(diǎn)的最大深度
max_locals 0x00 01 分配在當(dāng)前方法引用的局部變量表中的局部變量個(gè)數(shù)
code_length 0x00 00 00 05 當(dāng)前方法的code[]數(shù)組的字節(jié)數(shù)
code[5] 0x2A B7 00 01 B1 當(dāng)前方法的Java虛擬機(jī)字節(jié)碼
exception_table_length 0x00 00 給出了exception_table[]數(shù)組的成員個(gè)數(shù)量
attributes_count 0x00 01 Code屬性中attributes表的成員個(gè)數(shù)
  • ** Java虛擬機(jī)指令集**
    在對(duì)code進(jìn)行解析時(shí),其中有一項(xiàng)code[5] = 2A B7 00 01 B1,這些值代表的是Java虛擬機(jī)字節(jié)碼,分別對(duì)這5個(gè)指令進(jìn)行解釋。這個(gè)指令對(duì)應(yīng)的值,都可以在 Java虛擬機(jī)規(guī)范(Java SE 7)的第6章 Java虛擬機(jī)指令集搜索到。

  • 2A對(duì)應(yīng)的指令為aload_0,表示將第0個(gè)Slot中為reference類型的本地變量推送到操作數(shù)棧頂。我們通過LocalVariableTable來查看slot對(duì)應(yīng)的本地變量,第0個(gè)Slot中為reference類型的本地變量就是this


    aload

在前面的示例中,使用javap解析出來的結(jié)果中沒有包含LocalVariableTable字段,需要在使用javac編譯java中時(shí),加上-g的參數(shù),生成的class文件中才帶有LocalVariableTable的信息。

D:\TestClass>javac -g TestClass.java

D:\TestClass>javap -verbose TestClass
// 省略常量池部分
{
  public TestClass();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LTestClass;
}
  • B7對(duì)應(yīng)的指令為invokespecial,表示已棧頂?shù)膔eference類型的數(shù)據(jù)所指向的對(duì)象作為方法的接收者,調(diào)用此對(duì)象的實(shí)例構(gòu)造方法、private方法或者它的父類方法。invokespecial有一個(gè)u2類型的參數(shù)說明具體是調(diào)用的哪個(gè)方法。


    invokespecial
  • 00 01表示invokespecial的參數(shù),指向的是常量池中索引值為1的常量,即java/lang/Object."<init>":()V

  • B1對(duì)應(yīng)的指令為return,表示返回此方法,并且返回值為void。這條指令執(zhí)行后,當(dāng)前方法結(jié)束。


    Ireturn
  • 2.4 LineNumberTable_attribute

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

由于attribute_name_index指向的是常量池中的LineNumberTable,因此,該項(xiàng)attribute為LineNumberTable_attribute

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]; 
}
Option Values Description
attribute_name_index 0x0B 指向常量池中的第11個(gè)常量,LineNumberTable
attribute_length 0x00 06 當(dāng)前屬性的長度,不包括開始的6個(gè)字節(jié)
line_number_table_length 0x00 01 line_number_table[]數(shù)組的成員個(gè)數(shù)為1
start_pc 0x00 00 code[]數(shù)組在該索引處的字符表示源文件中新的行的起點(diǎn)
line_number 0x00 02 line_number項(xiàng)的值必須與源文件的行數(shù)相匹配

3. attributes_count、attributes

attribute_info表結(jié)構(gòu)如下:

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

由于attribute_name_index指向的是常量池中的SourceFile,因此,該項(xiàng)attribute為SourceFile_attribute

SourceFile_attribute { 
    u2 attribute_name_index; 
    u4 attribute_length; 
    u2 sourcefile_index; 
}
Option Values Description
attributes_count 0x00 01 屬性個(gè)數(shù)為1
attribute_name_index 0x00 0C 指向常量池中的SourceFile
attribute_length 0x00 00 02 屬性長度2
sourcefile_index 0x00 0D 執(zhí)行常量池中的TestClass.java
attributes字段

4. 參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容