當JVM運行Java程序的時候,它會加載對應的class文件,並提取class文件中的信息存放在JVM的方法區內存中。
Class文件組成
1、Class文件是一組以8位字節為基礎單位的二進制流,各個數據項目嚴格按順序緊湊排列在Class文件中,中間沒有分隔符。所以Class文件中存儲的內容幾乎全部是程序運行的必要數據。
2、當遇到占據8位字節以上空間的數據項時,會按照高位在前的方式, 分割成若干個8位字節進行存儲。
Class文件數據類型
無符號數
1、基本數據類型,以u1、u2、u4、u8分別代表1個字節、2個字節、4個字節、8個字節。
2、無符號數可以用來描述數字、索引引用、數量值和按照UTF8編碼構成字符串值。
表
1、表用來描述有層次關系的的復合數據結構。
2、表是由無符號數或者其他表作為數據項構成的復合數據結構。
3、所有表都以_info結尾。
4、整個class文件本質上就是一張表。
item1:magic(魔數)[u4]:0xCAFEBABE
辨別class文件與非class文件
item2:minor_version、major_version(次、主版本號) [u2,u2]
隨著Java技術的發展,class文件的格式會發生變化。版本號的作用在於使得虛擬機能夠認識當前加載class的文件格式。從而准確的提取class文件信息。
item3:常量池(constant_pool_count 、constance_pool)[u2,cp_info]
常量池內容
常量池存放兩大類常量:字面量(Literal)和符號引用(Synbolic Reference)
一、字面量
1、文本字符串
2、聲明為final的常量值
final修飾的變量有三種:靜態變量、實例變量和局部變量,分別表示三種類型的常量
3、基本數據類型
4、其他
二、符號引用
類和接口的全限定名(Fully Qualified Name)
字段的名稱和描述符(Descriptor)
方法的名稱和描述符
常量池結構
JVM會將每一個常量構成一個常量表,每個常量表都有自己的入口地址。而實際上在JVM會將這些常量表存儲在方法區中一塊連續的內存空間中,因此class文件會根據常量表在常量池中的位置對其進行索引。比如常量池中的第一個常量表的索引值就是1,第二個就是2。有的時候常量表A需要常量表B的內容,則在常量表A中會存儲常量表B的索引值x。而constant_pool_count就記錄了有多少個常量表,或則所有多少個索引值。實際上,常量池中沒有索引值為0的常量表,但這缺失的索引值也被記錄在 constant_pool_count中,因此 constant_pool_count等於常量表的數量加1。
常量池計數器(constant_pool_count):常量表(cp_info)數量+1。
常量表(cp_info):constant_pool_count-1個。
常量表結構(cp_info)
每個常量表(cp_info) 都會對應記錄著class文件中的某中類型的字面量
這14種表有一個共同的特點,就是開始的第一位是一個u1類型的標志位(tag,就是上表中的標志這一列),代表當前這個常量屬於哪種常量類型。
字符串常量池:http://www.cnblogs.com/tenghoo/p/jvm_string.html
基本類型常量池:http://www.cnblogs.com/tenghoo/p/jvm_primitive.html
item4:訪問標志(access_flags)[u2]
表明該class文件中定義的是類還是接口,訪問修飾符是public還是缺省。類或接口是否是抽象的。類是否是final的。
item5:類索引(this_class) [u2]、父類索引(super_class)[u2]、接口索引集合(inteface_count、interfaces )[u2,u2]
1、Class文件通過這三項數據來確定這個類的繼承關系。
2、類索引用於確定這個類的全限定名。
3、父類索引用於確定這個類的父類的全限定名。由於Java語言不允許多重繼承,所以父類索引只有一個。
4、除了java.lang.Object之外,所有的Java類都有父類,因此除了java.lang.Object外,所有Java類的父類索引都不為0。
5、接口索引集合就用來描述這個類實現了哪些接口。
6、類索引和父類索引用兩個u2類型的索引值表示,它們各自指向一個類型為CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info類型的常量中的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串。
7、對於接口索引集合,入口的第一項:u2類型的數據為接口計數器(interfaces_count),表示索引表的容量。如果該類沒有實現任何接口,則該計數器值為0,後面接口的索引表不再占用任何字節。
item6:字段表集合(field_count,fields) [u2,field_info]
1、字段表(field_info)用於描述接口或者類中聲明的變量。
2、字段(field)包括類級變量以及實例級變量,但不包括在方法內部聲明的局部變量。
field_info表格式
access_flags(2byte 訪問修飾符)
name_index(2byte 存儲字段名的常量表在常量池中的索引)
description_index(2byte 存儲字段的所屬類型的常量表在常量池中的索引)
attribute_count(2byte 屬性表的數量)
attribute (屬性)
其中attribute是由多個attribute_info組成。而JVM規范定義了字段的三種屬性:ConstanceValue、Deprecated和Synthetic。
item7:方法表集合(method_count、methods) [u2,method_info]
method_count、methods 與字段類似,method_count表明類中方法的數量和每個方法的常量表的索引。methods表明了不同長度的method_info表的序列。
method_info表格式
access_flags(2byte 訪問修飾符)
name_index(2byte 存儲方法名的常量表在常量池中的索引)
description_index(2byte 存儲方法的返回類型和參數類型的常量表在常量池中的索引)
attribute_count(2byte 屬性表的數量)
attribute (屬性)
其中方法的屬性JVM規定了四種:Code,Deprecated,Exceptions,Synthetic。
簡單名稱:方法名如:fun,字段名如:str1
全限定名:com/mobjia/clazz/TestClass
描述符:
描述符的作用是用來描述字段的數據類型、方法的參數列表(包括數量、類型以及順序)和返回值。
根據描述符規則,基本數據類型(byte、char、double、float、int、long、short、boolean)以及代表無返回值的void類型都用一個大寫字符來表示,而對象類型則用字符L加對象的全限定名來表示。
數組類型:每一維度將使用一個前置的“[”字符來描述,如一個定義為“java.lang.String[][]”類型的二維數組,將被記錄為:“[[Ljava/lang/String;”,一個整型數組“int[]”將被記錄為“[I”。
用描述符來描述方法時,按照先參數列表,後返回值的順序描述,參數列表按照參數的嚴格順序放在一組小括號“()”之內。
void inc()的描述符為()V
java.lang.String toString()的描述符()Ljava/lang/String;
int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,inttargetCount,int fromIndex)的描述符 ([CII[CIII)I
Class實例分析
工具:WinHe十六進制編輯器
參考:
http://blog.csdn.net/luanlouis/article/details/39960815
http://blog.csdn.net/a616413086/article/details/52250638
http://hxraid.iteye.com/blog/687660