方法表:
在上一次咱們已經分析到了字段信息了,如下:
緊接著就是方法相關的信息了:
而它展開之后的結構為:
所以往后數2個字節,看一下方法的總數:
3個方法,可咱們只定義了兩個方法呀:
因為編譯器會為我們生成一個默認的構造方法,所以就3個了,那每個方法的具體信息是啥呢?它是一個method_info類型的,如下:
也就是方法表,當然也有它自己的一個結構,下面來看一下:
- access_flags:占用兩個字節,表示訪問標記。
- name_index:占用兩個字節,名字索引,指向的是常量池。
- descriptor_index:占用兩個字節,描述索引,指賂的是常量池。
- attributes_count:占用兩個字節,屬性個數,如果為0,則下面的屬性表就不顯示了。
- attributes::屬性表。
用結構形式來表示:
那按照上面的表先來看第一個方法的訪問標記,往后讀兩個字節:
查看下訪問修飾符表,對應于:
表示是一個public的方法,接下來兩個字節則表示方法名字索引,走著:
對應常量池:
再往下二個字節則表示描述符索引:
對應常量池:
說明該方法是一個默認構造方法。從javap -verbose中也能對應上:
屬性表:
接下來二個字節為屬性個數:
表示有一個屬性,所以屬性表中的個數也為1,而屬性表是attribute_info類型,很顯然也有它自己的結構,那長啥樣呢?
- attribute_name_index:占2個字節,表示屬性名字的索引,指向常量池。
- attribute_length:占4個字節,表示屬性的長度。
- info[attribute_length]:占1個字節,表示具體的信息。
依照上面的順序,先數2個字節:
對應常量池:
其實在javap -verbose中也能看到每個方法都有一個Code字樣,如下:
Constant pool:#1 = Methodref #4.#20 // java/lang/Object."<init>":()V#2 = Fieldref #3.#21 // com/jvm/bytecode/MyTest1.a:I#3 = Class #22 // com/jvm/bytecode/MyTest1#4 = Class #23 // java/lang/Object#5 = Utf8 a#6 = Utf8 I#7 = Utf8 <init>#8 = Utf8 ()V#9 = Utf8 Code#10 = Utf8 LineNumberTable#11 = Utf8 LocalVariableTable#12 = Utf8 this#13 = Utf8 Lcom/jvm/bytecode/MyTest1;#14 = Utf8 getA#15 = Utf8 ()I#16 = Utf8 setA#17 = Utf8 (I)V#18 = Utf8 SourceFile#19 = Utf8 MyTest1.java#20 = NameAndType #7:#8 // "<init>":()V#21 = NameAndType #5:#6 // a:I#22 = Utf8 com/jvm/bytecode/MyTest1#23 = Utf8 java/lang/Object {public com.jvm.bytecode.MyTest1();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: iconst_16: putfield #2 // Field a:I9: returnLineNumberTable:line 3: 0line 4: 4LocalVariableTable:Start Length Slot Name Signature0 10 0 this Lcom/jvm/bytecode/MyTest1;public int getA();descriptor: ()Iflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: getfield #2 // Field a:I4: ireturnLineNumberTable:line 7: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lcom/jvm/bytecode/MyTest1;public void setA(int);descriptor: (I)Vflags: ACC_PUBLICCode:stack=2, locals=2, args_size=20: aload_01: iload_12: putfield #2 // Field a:I5: returnLineNumberTable:line 11: 0line 12: 5LocalVariableTable:Start Length Slot Name Signature0 6 0 this Lcom/jvm/bytecode/MyTest1;0 6 1 a I } SourceFile: "MyTest1.java"
那它表示啥意思呢?其實是表示方法執行的代碼,指的是:
當然啦在字節碼文件中不可能是跟源文件中看到的一樣,而是通過了一些助記符進行了處理,如下:
這個在未來進行詳細學習的,好,繼續來分析屬性,接下來4個字節表示屬性的長度,如下:
說明屬性的長度為56,然后最后一個字節表示info信息,也就是code的具體信息,這塊是比較復雜的,下面先來了解一些理論:
- JVM預定義了部分attribute,但是編譯器自己也可以實現自己的attribute寫入class文件里,供運行時使用。
- 不同的attribute通過attribute_name_index來區分。
其中JVM預定義的attribute為如下表:
Code結構:
這部分東東是比較多的,這次只先對其結構有個初步了解既可,它的作用是保存該方法的結構,如所對應的字節碼:
- attribute_length表示attribute所包含的字節數,不包含attribute_name_index和attribute_length字段。
- max_stack表示這個方法運行的任何時刻所能達到的操作數棧的最大深度。
- max_locals表示方法執行期間創建的局部變量的數目,包含用來表示傳入的參數的局部變量。
- code_length表法該方法所包含的字節碼的字節數以及具體的指令碼。
- 具體的字節碼既是該方法被調用時,虛擬機所執行的字節碼。
- exception_table:這里存放的是處理異常的信息。
- 第一個exception_table表項由start_pc、end_pc、handler_pc、catch_type組成。
- start_pc和end_pc表示在code數組中的從start_pc到end_pc處(包含start_pc,不包含end_pc)的指令拋出的異常會由這個表項來處理。
- handler_pc表示處理異常的代碼的開始處。catch_type表示會被處理的異常類型,它指向常量池里的一個異常類。當catch_type為0時,表示處理所有的異常。
這么多陌生的字段,直接暈掉,木要著急,先有個大概了解,在未來學習中會吃透它的,好,先來回到字節碼中繼續分析,其中code中屬性的長度為56:
接下來2個字節表示max_stack:
再接下來2個字節表示max_locals:
對應javap -verbose:
接下來4個字節表示code的長度:
code_length=10,而此時發現在javap -verbose中貌似木有找到對應的:
那接下來的分析沒有了參照就不知道我們自己分析的對不對了,對于學習效果會大打折扣了,此時就得借助于另外一個工具來參照了,該工具為jclasslib,gitbub地址:https://github.com/ingokegel/jclasslib,它顯示的信息就會比javap -verbose要詳細很多,訪問一下官網:
它包含獨立的軟件和IntelliJ IDEA插件化的方式,所以都裝一下,先下載mac安裝包:
?
具體安裝就不概述了,裝好之后用它來打開我們的字節碼文件既可,長這樣:
同時可以給IDE裝上插件,更加便于分析,如下:
安裝好之后,直接就可以在當前打開的java文件中執行這個菜單選項既可:
看到的效果跟獨立的軟件看到的是一樣的,好,下面來用這個新工具來瞅一眼看到的信息:
對比下javap -verbose:
差不多,不過jclasslib工具可以看到JDK的版本,接下來就是常量池:
但實際是只有23個,展開看一下:
對于javap -versbose:
明顯要豐富許多,繼續往下看:
其中是可以直接點擊鏈到對應的常量池的,如下:
接著就是常量池的信息了:
展開之后,索引都能鏈接上去,非常之方便:
接著就是接口信息,目前木有接口:
然后就到了字段信息了,目前只有一個字段:
然后再是方法信息,有三個方法:
點擊其中一個看一下:
有code信息:
跟javap -versbose是對應上的:
最后是附加信息:
LineNumberTable:這個屬性用來表示code數組中的字節碼和Java代碼行數之間的關系。這個屬性可以用來在調試的時候定位代碼執行的行數。比如說程序拋異常了,而程序執行的是字節碼文件,怎么我們就能看到具體報錯在源碼中的行數呢,其實就是通過該信息做到的。
而它的結構體為:
跟javap -verbose中是能對應上的:
最后則是類的屬性了:
可見jclasslib的結構跟咱們理論上看到的是一模一樣的,所以有了它也能讓我們在未來學習code這塊的結構更加清晰,這是javap -verbose不能達到的。