class类文件结构
目錄
- Class 類文件的結構
- 幻數和Class文件版本
- 常量池
- 訪問標志
- 類索引
- 父類索引
- 接口索引集合
- 字段表集合
- 訪問標識符
- 簡單名和描述符
- 方法表集合
- 屬性表
- class文件屬性表
- 腳注
- 參考資料
Class 類文件的結構
由java源碼編譯后的class文件包含了Java虛擬機指令和符號表以及若干其他輔助信息。class文件是一組以8位比特(1字節)為單位的二進制流,按照大端順序存儲,嚴格按照順序依次排列下去。
class文件采用類似c語言結構體的偽結構存儲數據,這種結構只有兩種數據類型,無符號數和表。
無符號數是基本數據類型,可以表示整型、浮點型和字符串字面值等。用u1,u2,u4,u8分別表示1個字節,2個字節,4個字節和8個字節的無符號數。
表是由多個無符號數和其他表組合成的復合類型。表類型一般以_info結尾。
整個class文件結構如下表格1:
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count-1 |
| u2 | access_flags | 1 |
| u2 | this_class | 1 |
| u2 | super_class | 1 |
| u2 | interfaces_count | 1 |
| u2 | interfaces | interfaces_count |
| u2 | fields_count | 1 |
| field_info | fields | fields_count |
| u2 | methods_count | 1 |
| method_info | methods | methods_count |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
class文件開始處四個字節表示magic,緊接著兩個字節表示minor_version和兩個字節表示major_version,緊接著兩個字節表示常量池數目,緊接著是常量池數目-1個不同結構的常量表,依次類推。
寫一個簡單的程序,編譯后分析字節碼。
源碼:
package com.hnzhrh;public class Simple{private int sum = 0;public int add(int a,int b){sum = a + b;return sum;} }得到的class文件以十六進制查看:
幻數和Class文件版本
class文件格式表格第一行,u4類型的magic一個。
0x0000+0~0x0000+3:CA FE BA BE
該值表示該文件是能被JVM接收的class文件。使用幻數區分文件類型而不使用文件后綴名,文件后綴名是可以任意更改的(把jpg改成txt照常還能用畫圖打開,使用幻數區分)。
class文件格式表格第二行,u2類型的minor_version一個。
0x0000+4~0x0000+5:00 00
表示次版本號為0。
class文件格式表格第三行,u2類型的major_version一個。
0x0000+6~0x0000+7:00 34
表示主版本號為52。
常量池
常量池主要存放兩大類常量:字面量和符號引用。
符號引用包括了下面三種常量:
- 類和接口的全限定名
- 字段的名稱
- 字段的描述符
- 方法的名稱
- 方法的描述符
class文件格式表格第四行,u2類型的constant_pool_count一個。
0x0000+8~0x0000+9:00 13
表示常量池中常量計數,數值為19。但常量項有18個,該計數是從1開始的 。空出來的第0項用來滿足特定情況下不引用常量池項目的情況,可以指向常量池0項。
緊接著常量計數之后,就是18個常量項,每個常量項都有自己獨立的表結構,表結構的開頭tag標志表明了常量類型,如下表2所示:
0x0000+10:0A
查看表可知,tag標志為10(0x0A)的常量類型是CONSTANT_Methodref_info,該類型大小為5字節,所以有:
0x0000+10~0x0000+14 為一個CONSTANT_Methodref_info類型常量,也是一個表結構。
通過表查看CONSTANT_Methodref_info類型的結構,該結構是由u1類型的tag,u2類型的index(指向聲明方法的類描述符CONSTANT_Class_info的索引項)和u2類型的index(指向名稱及類型描述符CONSTANT_NameAndType的索引項)組成的。
將0x0000+10~0x0000+14劃分成三部分內容:
0x0000+10:0A u1類型的tag,值為10。
0x0000+11~0x0000+12:00 04 u2類型的index,值為4。
0x0000+13~0x0000+14:00 0F u2類型的index,值為15。
為了直觀,建立一個表格,#+數字表示常量池項的索引,后面單元格存放常量項結構的內容。如下所示:
| #0 | |||
| #1 | 10 | #4 | #15 |
其余常量項跟上述類似,最后得到的完整的常量池的直觀表格:
可讀這一列的分析以#1的常量項為例:
至此,常量池的內容結束(0x0000+8~0x0160+5)。
訪問標志
class文件格式表格第6行,u2類型的access_flags一個。
0x0160+6~0x0160+7:00 21
訪問標志用來表示這個class是類還是接口,是否定義為public,是否定義為abstract等。具有多個屬性則將多個標志值或運算得到的結果作為訪問標志。
訪問標志表3:
| ACC_PUBLIC | 0x0001 | 是否為public |
| ACC_FINAL | 0x0010 | 是否為final,只有類可設置 |
| ACC_SUPER | 0x0020 | JDK 1.0.2之后編譯出來的類的這個標志為真 |
| ACC_INTERFACE | 0x0200 | 標識是接口 |
| ACC_ABSTRACT | 0x0400 | 是否為abstract,接口和抽象類為真 |
| ACC_SYNTHETIC | 0x1000 | 標識這個類并非由用戶代碼產生 |
| ACC_ANNOTATION | 0x2000 | 標識這是一個注解 |
| ACC_ENUM | 0x4000 | 標識這是一個枚舉 |
該class訪問標志為0x 0021,表示是一個public類。
類索引
class文件格式表第7行,u2類型的this_class一個。
0x0160+8~0x0160+9:00 03
類索引為0x0003,即指向常量池#3,是一個Class_info類型的常量,表示類或接口的符號引用,該常量的index指向#17,即com/hnzhrh/Simple。
類索引用來確定該類的全限定名。
父類索引
class文件格式表第8行,u2類型的super_class一個。
0x0160+10~0x0160+11:00 04
父類索引為Ox0004,即指向常量池#4,同上分析,得到java/lang/Object。
父類索引用來確定這個類的父類的全限定名。
接口索引集合
class文件格式第9行,u2類型的interface_count一個。
0x0160+12~0x0160+13:00 00
表示接口計數器,該值為0,則該類沒有實現接口。所以class文件格式第10行的interfaces也沒有。
類索引,父類索引和接口索引集合三者可以表明該類或接口的繼承關系。
字段表集合
class文件格式第11行,u2類型的fields_count一個。
0x0160+14~0x0160+15:00 01
表示有1個字段表。
字段表不像常量項那樣各有各的結構,字段表的結構是統一的,如下表4所示:
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
類似上述分析方法,得到如下可讀表格:
| u2 | access_flags | 1 | 0x00 02 | private |
| u2 | name_index | 1 | 0x00 05 | sum |
| u2 | descriptor_index | 1 | 0x00 06 | I |
| u2 | attributes_count | 1 | 0x00 00 | |
| attribute_info | attributes | attributes_count |
字段類型用于描述接口或者類中聲明的變量。
訪問標識符
access_flags可以表明字段所具有的特性,access_flags由下表5所示:
| ACC_PUBLIC | 0x0001 | 是否為public |
| ACC_PRIVATE | 0x0002 | 是否為private |
| ACC_PROTECTED | 0x0004 | 是否為protected |
| ACC_STATIC | 0x0008 | 是否為static |
| ACC_FINAL | 0x0010 | 是否為final |
| ACC_VOLATILE | 0x0040 | 是否為volatile |
| ACC_TRANSIENT | 0x0080 | 是否為transient |
| ACC_SYNTHETIC | 0x1000 | 是否由編譯器自動生成 |
| ACC_ENUM | 0x4000 | 是否為enum |
簡單名和描述符
name_index和descriptor_index兩者都為常量池的索引。
name_index為字段的簡單名索引(即不帶有包名的名稱)。
描述符的作用是用來描述字段的數據類型,方法的參數列表(包括數量、類型和順序)和返回值。
基本數據類型和表示無返回值的void的描述符都用一個大寫字母來表示,對象類型則用L加對象的全限定名表示,如下表6:
| B | byte |
| C | char |
| D | double |
| F | float |
| I | int |
| J | long |
| S | short |
| Z | boolean |
| V | void |
| L | 對象類型 |
對于數組類型,使用一個前置的"["來描述,比如定義一個java.lang.String[][]類型的二維數組,將被記錄為[Ljava/lang/String;。
字段表集合中不會列出從超類或父接口中繼承而來的字段,但有可能列出原本Java代碼中不存在的字段,比如在內部類中為了保持對外部類的訪問性,會自動添加指向外部類實例的字段。
方法表集合
class文件格式第13行,u2類型的methods_count一個。
0x0176+8~0x0176+9:00 02
表示有兩個方法表。
方法表結構與字段表結構一致,如下表7:
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
方法訪問標志與字段訪問標志略有不同,如下表8:
| ACC_PUBLIC | 0x0001 | 是否為public |
| ACC_PRIVATE | 0x0002 | 是否為private |
| ACC_PROTECTED | 0x0004 | 是否為protected |
| ACC_STATIC | 0x0008 | 是否為static |
| ACC_FINAL | 0x0010 | 是否為final |
| ACC_SYNCHRONIZED | 0x0020 | 是否為synchronized |
| ACC_BRIDGE | 0x0040 | 是否為編譯器產生的橋接方法 |
| ACC_VARARGS | 0x0080 | 是否接受不定參數 |
| ACC_NATIVE | 0x0100 | 是否為native |
| ACC_ABSTRACT | 0x0400 | 是否為abstract |
| ACC_STRICTFP | 0x0800 | 是否為strictfp |
| ACC_SYNTHETIC | 0x1000 | 是否由編譯器自動生成 |
如果父類方法沒有被重寫,方發表中不會出現來自父類的方法,也有可能出現編譯器自動添加的方法,如類構造器<clinit>和實列構造器<init>方法。
開始分析方法表集合:
0x0176+10~0x0176+11:00 01
該方法是public。
0x0176+12~0x0176+13:00 07
簡單名索引為7,即<init>。
0x0176+14~0x0176+5:00 08
描述符索引為8,即V()。
0x0192+0~0x0192+1:00 01
屬性個數為1。
接下來就是一個屬性表。
屬性表
在class文件、字段表和方法表都可以攜帶屬性表集合,用于描述某些場景專有的信息。
屬性表開頭都是u2類型的屬性名稱索引和u4類型的屬性長度,緊接著就是不同屬性的結構了。
屬性表中可能包含其他屬性表。
緊接著上述分析:
0x0192+2~0x0192+3:00 09
屬性名稱索引為9,即Code屬性。
0x0192+4~0x0192+7:00 00 00 26
該屬性的長度為38字節。
Code屬性表9結構:
| 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 |
同上分析,可以得到:
max_stack:2,max_locals:1
max_stack代表了操作數棧深度的最大值,虛擬機運行時根據這個值來分配棧幀中操作數棧大小。
max_locals代表了局部變量表所需的存儲空間,在這里是隱式傳遞給方法的this參數。
code_length:10
表示字節碼長度。
code:10字節長的字節碼指令,2A B7 00 01 2A 03 B5 00 02 B1
exception_table_length:0
attributes_count:1
表示Code屬性中還有1個屬性。
緊接著就是這個屬性的名稱索引和長度,名稱索引:10,即LineNumberTable,長度:10。
LineNumberTable屬性用于描述Java源行號和字節碼偏移量之間的對應關系。
LineNumberTable屬性表10結構:
| 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表包含了兩個u2類型的數據項,第一個表示字節碼行號,第二個表示Java源碼行號。
line_number_table_length : 2
line_number_infor: 0 : 3 和 4 : 4。
至此,<init>方法結束。
開始分析下一個方法,分析方法類似,分析結果如下表:
| u2 | access_flags | 0x00 01 | public |
| u2 | name_index | 0x00 0B | add |
| u2 | descriptor_index | 0x00 0C | (II)I |
| u2 | attributes_count | 0x00 01 | 1個屬性 |
| u2 | attribute_name_index | 0x00 09 | Code |
| u4 | attribute_length | 0x00 00 00 28 | 屬性長度40字節 |
| u2 | max_stack | 0x00 03 | |
| u2 | max_locals | 0x00 03 | |
| u4 | code_length | 0x00 00 00 0C | 字節碼長度12字節 |
| u1 | code | 2A 1B 1C 60 B5 00 02 2A B4 00 02 AC | 字節碼 |
| u2 | exception_table_length | 0x00 00 | |
| exception_info | exception_table | 無 | |
| u2 | attributes_count | 0x00 01 | Code屬性的屬性 |
| u2 | attribute_name_index | 0x00 0A | LineNumberTable |
| u4 | attribute_length | 0x00 00 00 0A | 屬性長度10字節 |
| u2 | line_number_table_length | 0x00 02 | 有兩個line_number_info |
| u2 | start_pc | 0x00 00 | 字節碼偏移量 |
| u2 | line_number | 0x00 06 | 源碼行號 |
| u2 | start_pc | 0x00 07 | |
| u2 | line_number | 0x00 07 |
class文件屬性表
在class文件、字段表和方法表都可以攜帶屬性表集合,用于描述某些場景專有的信息。
屬性表開頭都是u2類型的屬性名稱索引和u4類型的屬性長度,緊接著就是不同屬性的結構了。
屬性表中可能包含其他屬性表。
class文件格式表格第15行,u2類型的attributes_count一個。
0x0288+4~0x0288+5:00 01
表示class文件有1個屬性表。
分析方法類似,結果如下:
| u2 | attribute_name_index | 0x00 0D | SourceFile |
| u4 | attribute_length | 0x00 00 00 02 | 屬性長度2字節 |
| u2 | sourcefile_index | 0x00 0E | Simple.java |
腳注
參考資料
深入理解Java虛擬機 JVM高級特性與最佳實踐 周志明著 第二版
表格內容來源于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-1?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-3和表6-6?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-7?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-8?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-9?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-10?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-11?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-12?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-15?
表格內容來源于于《深入理解Java虛擬機 JVM高級特性與最佳實踐》第2版 周志明著 第六章表6-18?
轉載于:https://www.cnblogs.com/RohanZhang/p/9510863.html
總結
以上是生活随笔為你收集整理的class类文件结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU5977 Garden of E
- 下一篇: 洛谷 4568 [JLOI2011] 飞