java class文件結構

本文轉載自:http://hi.baidu.com/etcjqvnjombopzq/item/19d47145958b6594823ae12f

圖 3. ASM – Java 類文件

從上圖中可以看到,一個 Java 類文件大致可以歸爲 10 個項:

  • Magic:該 項存放了一個 Java 類文件的魔數(magic number)和版本信息。一個 Java 類文件的前 4 個字節被稱爲它的魔數。每個正確的 Java 類文件都是以 0xCAFEBABE 開頭的,這樣保證了 Java 虛擬機能很輕鬆的分辨出 Java 文件和非 Java 文件。
  • Version:該項存放了 Java 類文件的版本信息,它對於一個 Java 文件具有重要的意義。因爲 Java 技術一直在發展,所以類文件的格式也處在不斷變化之中。類文件的版本信息讓虛擬機知道如何去讀取並處理該類文件。
  • Constant Pool:該 項存放了類中各種文字字符串、類名、方法名和接口名稱、final 變量以及對外部類的引用信息等常量。虛擬機必須爲每一個被裝載的類維護一個常量池,常量池中存儲了相應類型所用到的所有類型、字段和方法的符號引用,因此 它在 Java 的動態鏈接中起到了核心的作用。常量池的大小平均佔到了整個類大小的 60% 左右。
  • Access_flag:該項指明瞭該文件中定義的是類還是接口(一個 class 文件中只能有一個類或接口),同時還指名了類或接口的訪問標誌,如 public,private, abstract 等信息。
  • This Class:指向表示該類全限定名稱的字符串常量的指針。
  • Super Class:指向表示父類全限定名稱的字符串常量的指針。
  • Interfaces:一個指針數組,存放了該類或父類實現的所有接口名稱的字符串常量的指針。以上三項所指向的常量,特別是前兩項,在我們用 ASM 從已有類派生新類時一般需要修改:將類名稱改爲子類名稱;將父類改爲派生前的類名稱;如果有必要,增加新的實現接口。
  • Fields:該項對類或接口中聲明的字段進行了細緻的描述。需要注意的是,fields 列表中僅列出了本類或接口中的字段,並不包括從超類和父接口繼承而來的字段。
  • Methods:該 項對類或接口中聲明的方法進行了細緻的描述。例如方法的名稱、參數和返回值類型等。需要注意的是,methods 列表裏僅存放了本類或本接口中的方法,並不包括從超類和父接口繼承而來的方法。使用 ASM 進行 AOP 編程,通常是通過調整 Method 中的指令來實現的。
  • Class attributes:該項存放了在該文件中類或接口所定義的屬性的基本信息。

以下轉載自:http://edu.cryes.com/program/java/27962.html
Java文件結構用類似struct的描述如下:

ClassFile {
u4 magic; // 必須爲: 0xCAFEBABE
u2 minor_version;
u2 major_version; //CLASS文件結構主次版本號 JAVA2支持45.0-46.0
u2 constant_pool_count; //記錄常量信息
cp_info constant_pool[constant_pool_count-1]; //計數從1開始
u2 access_flags; //class/interface訪問權限
u2 this_class; //指向constant_poll中的有效索引值
u2 super_class; //0或指向constant_poll中的有效索引值,對於interface必須爲非0
u2 interfaces_count; //superinterfaces的個數
u2 interfaces[interfaces_count]; //計數[0,count-1) 對應constant_pool中的一個索引值
u2 fields_count; 
field_info fields[fields_count]; //主要用於記錄class及實例中的變量
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

cp_info {
u1 tag; 
u1 info[]; 

tag 意義如下: 

CONSTANT_Class 7 
CONSTANT_Fieldref 9 
CONSTANT_Methodref 10 
CONSTANT_InterfaceMethodref 11 
CONSTANT_String 8 
CONSTANT_Integer 3 
CONSTANT_Float 4 
CONSTANT_Long 5 
CONSTANT_Double 6 
CONSTANT_NameAndType 12 
CONSTANT_Utf8 1 

此時cp_info分別對應結構變化爲
1. CONSTANT_Class 
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}

2. CONSTANT_Fieldref
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index; //constant_pool的索引,對應CONSTANT_Class_info 
u2 name_and_type_index;//constant_pool的索引,對應CONSTANT_NameAndType_info 
}

3. CONSTANT_Methodref
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}

4. CONSTANT_InterfaceMethodref
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}

5. CONSTANT_String
CONSTANT_String_info {
u1 tag;
u2 string_index;
}

6. CONSTANT_Integer
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}


7. CONSTANT_Float
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}

8. CONSTANT_Long
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}

9. CONSTANT_Double
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes
}

10.CONSTANT_NameAndType
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}

11.CONSTANT_Utf8
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
access_flags意義如下:

ACC_PUBLIC 0x0001 
ACC_FINAL 0x0010 
ACC_SUPER 0x0020 
ACC_INTERFACE 0x0200 
ACC_ABSTRACT 0x0400 
如果是interface那麼必須置ACC_INTERFACE,如果沒有置ACC_INTERFACE則定義的是一個類而非接口。
如果設置了ACC_INTERFACE,那麼ACC_ABSTRACT位也必須被設置,當然也可以設置ACC_PUBLIC。
ACC_SUPER用以表明invokespecial語義,Sun公司老的JAVA編譯器沒有設置ACC_SUPER,並且老的JVM
忽略ACC_SUPER位,但新的編譯器應該實現invokespecial語義。
其他未指明的位保留將來使用,並且編譯器應當將其置爲0,同時Java虛擬機應當忽略他們。

this_class: constant_pool中的索引值,指向的元素的cp_info等價爲CONSTANT_Class_info 

CONSTANT_Class_info {
u1 tag; //必須爲CONSTANT_Class (7)
u2 name_index; //爲指向constant_pool中的一個索引值
}

name_index :指向的元素的cp_info等價爲CONSTANT_Utf8_info 

CONSTANT_Utf8_info {
u1 tag; //必須爲CONSTANT_Utf8 (1)
u2 length; 
u1 bytes[length]; //Utf8編碼的字符串
}


field_info {
u2 access_flags; //訪問控制權
u2 name_index; //constant_pool中的索引,對應於CONSTANT_Utf8_info描述。
u2 descriptor_index; //constant_pool中的索引,對應於CONSTANT_Utf8_info描述。
u2 attributes_count;
attribute_info attributes[attributes_count]; //attribute_info將在mothods後描述。
}
field_info中access_flages意義如下:

ACC_PUBLIC 0x0001
ACC_PRIVATE 0x0002
ACC_PROTECTED 0x0004
ACC_STATIC 0x0008
ACC_FINAL 0x0010
ACC_VOLATILE 0x0040
ACC_TRANSIENT 0x0080

其中很顯然不能同時爲ACC_FINAL和ACC_VOLATILE ;且前三項是互斥的。
interface必須置ACC_PUBLIC, ACC_STATIC,ACC_FINAL位,且不能置其他位。
其他未指明的位保留將來使用,並且編譯器應當將其置爲0,同時Java虛擬機應當忽略他們。


methods指明瞭類中的所有方法。

method_info {
u2 access_flags;
u2 name_index; //指向constant_pool的入口,對應爲CONSTANT_Utf8_info 
u2 descriptor_index; //指向constant_pool的入口,對應爲CONSTANT_Utf8_info
u2 attributes_count;
attribute_info attributes[attributes_count];
//此處只能出現Code、Exceptions、Synthetic、Deprecated四種類型的屬性
}

access_flags訪問權描述如下:

ACC_PUBLIC 0x0001 
ACC_PRIVATE 0x0002 
ACC_PROTECTED 0x0004 
ACC_STATIC 0x0008 
ACC_FINAL 0x0010 
ACC_SYNCHRONIZED 0x0020 
ACC_NATIVE 0x0100 
ACC_ABSTRACT 0x0400 
ACC_STRICT 0x0800 

attribute_info {
u2 attribute_name_index; //constant_pool中的索引,對應於CONSTANT_Utf8_info描述。
u4 attribute_length;
u1 info[attribute_length];
}

現在已經預定義的屬性有:

1. SourceFile : attribute_info被替代爲:

SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index; //指向constant_pool中的一個CONSTANT_Utf8_info 結構。
}

2. ConstantValue : attribute_info被替代爲:

ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length; //必須爲2
u2 constantvalue_index;
}

對於constantvalue_index意義如下:
long CONSTANT_Long 
float CONSTANT_Float 
double CONSTANT_Double 
int, short, char, byte, boolean CONSTANT_Integer 
String CONSTANT_String 

ConstantValue用於field_info 中,用於描述一個static常量,
且此時field_info的access_flags應爲ACC_STATIC 


3. Code : attribute_info被替代爲:

Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack; //執行此函數時可用的棧的最大深度
u2 max_locals; //執行此函數可用到的最大本地變量數目,包括參數。
// 注意:一個long/double相當於2個變量數目.
u4 code_length; //本函數用到的代碼長度。
u1 code[code_length]; //實現本函數的真正字節碼
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc; //捕獲違例時執行代碼數組中的[start_pc, end_pc)部分
u2 handler_pc; //現在還不大明白他是幹嘛的!!
u2 catch_type; //指向constant_pool的索引,對應CONSTANT_Class_info 
}exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

CONSTANT_Class_info {
u1 tag; //必須爲CONSTANT_Class (7)
u2 name_index; //不用我再說了吧?
}

Code屬性用於method_info結構中。

4. Exceptions : attribute_info被替代爲:

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

5. InnerClasses : attribute_info被替代爲:

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];
}

6. Synthetic : attribute_info被替代爲:

Synthetic_attribute {
u2 attribute_name_index; //不用廢話了吧?
u4 attribute_length; //必須爲0
}
Synthetic用在 field_info、 method_info 中,
一個沒有出現在源程序中的變量必須使用Synthetic標記。

7. LineNumberTable : attribute_info被替代爲:

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];
}
LineNumberTable用於Code屬性中,通常用於調試。


8. LocalVariableTable : attribute_info被替代爲:

LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length; //當解釋到代碼數組的[start_pc,start_pc+length]
//時變量必須被賦值??
u2 name_index;
u2 descriptor_index;
u2 index; //到本地變量數組的一個索引
} local_variable_table[local_variable_table_length];


9. Deprecated : attribute_info被替代爲:

Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length; //必須爲0
}


當然你也可以定義自己的屬性,但要你自己的編譯器和虛擬機實現。JVM將忽略自己不認可的屬性。

來實踐一下吧!
編寫一個最簡單的程序:
class Test 
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}

c:\work>javac Test.java

c:\work>filedump Test.class

File Dump V0.3 Beta by cloud ([email protected]).

01:00 ca fe ba be 00 03 00 2d 00 1d 0a 00 06 00 0f 09 .......-........
01:01 00 10 00 11 08 00 12 0a 00 13 00 14 07 00 15 07 ................
01:02 00 16 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 .....<init>...()
01:03 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e V...Code...LineN
01:04 75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69 umberTable...mai
01:05 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 n...([Ljava/lang
01:06 2f 53 74 72 69 6e 67 3b 29 56 01 00 0a 53 6f 75 /String;)V...Sou
01:07 72 63 65 46 69 6c 65 01 00 09 54 65 73 74 2e 6a rceFile...Test.j
>d
02:00 61 76 61 0c 00 07 00 08 07 00 17 0c 00 18 00 19 ava.............
02:01 01 00 0c 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 07 ...Hello World!.
02:02 00 1a 0c 00 1b 00 1c 01 00 04 54 65 73 74 01 00 ..........Test..
02:03 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 .java/lang/Objec
02:04 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 t...java/lang/Sy
02:05 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a 61 stem...out...Lja
02:06 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 va/io/PrintStrea
02:07 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72 69 m;...java/io/Pri
>d
03:00 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e 74 ntStream...print
03:01 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 ln...(Ljava/lang
03:02 2f 53 74 72 69 6e 67 3b 29 56 00 20 00 05 00 06 /String;)V. ....
03:03 00 00 00 00 00 02 00 00 00 07 00 08 00 01 00 09 ................
03:04 00 00 00 1d 00 01 00 01 00 00 00 05 2a b7 00 01 ............*...
03:05 b1 00 00 00 01 00 0a 00 00 00 06 00 01 00 00 00 ................
03:06 01 00 09 00 0b 00 0c 00 01 00 09 00 00 00 25 00 ..............%.
03:07 02 00 01 00 00 00 09 b2 00 02 12 03 b6 00 04 b1 ................
>d
04:00 00 00 00 01 00 0a 00 00 00 0a 00 02 00 00 00 05 ................
04:01 00 08 00 06 00 01 00 0d 00 00 00 02 00 0e ..............
>

解讀一下:

ca fe ba be magic
00 03 00 2d 次主版本號,換算一下: 45.3 (注意,不是高字節在前,別犯職業病!)
00 1d constant_pool元素個數加一 : 29 那麼constant_pool就是[1-28]
constant_pool: 1-28

1. 0a 00 06 00 0f 
0x0a :CONSTANT_InterfaceMethodref 0x06 :class index 0x0f :name-type-index
2. 09 00 10 00 11
0x09 : CONSTANT_Fieldref 0x10: . . . 0x11 : . . . .
3. 08 00 12 0x08 : CONSTANT_String 0x12 : string_index
4. 0a 00 13 00 14 0x0a同於1.
5. 07 00 15 0x07 : CONSTANT_Class 0x15 : name_index
6. 07 00 16 
7. 01 00 06 3c 69 6e 69 74 3e 01 ...<init> 
0x01 CONSTANT_Utf8 0x06 : string length "<init>" : 構造函數
8. 01 00 03 28 29 56 ...()V 函數,無參數
0x01 . . . . . . 0x03 : . . . . "()V" : . . .
9. 01 00 04 43 6f 64 65 ...Code
10. 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 ...LineNumberTable
11. 01 00 04 6d 61 69 6e ...main
12. 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 
...([Ljava/lang/String;)
V 函數,參數爲String[]類型
13. 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 ...SourceFile
14. 01 00 09 54 65 73 74 2e 6a 61 76 61 ...Test.java
15. 0c 00 07 00 08 0x0c:CONSTANT_NameAndType 07 : name-index 08:name-type-index
16. 07 00 17
17. 0c 00 18 00 19
18. 01 00 0c 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 ...Hello World!
19. 07 00 1a
20. 0c 00 1b 00 1c
21. 01 00 04 54 65 73 74 ...Test
22. 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 ...java/lang/Object
23. 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d ...java/lang/System
24. 01 00 03 6f 75 74 ...out
25. 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b
...Ljava/io/PrintStream;
26. 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d...java/io/PrintStream
27. 01 00 07 70 72 69 6e 74 6c 6e ...println
28. 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56
...(Ljava/lang/String;)V


00 20 access_flags 
00 05 this_class
00 06 super_class
00 00 interfaces_count
00 00 fields_count
00 02 methods_count

methods[2]:

method_info {
u2 access_flags;
u2 name_index; 
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
0. 00 00 access_flags;
00 07 name_index; 看看constant_pool的第7項: <init> 表明當前描述構造函數
00 08 descriptor_index;
00 01 attributes_count;
00 09 attribute_name_index 0x09 看看constant_pool的第9項,簡單明瞭Code !! 
00 00 00 1d attribute_length = 29
00 01 max_stack
00 01 max_locals
00 00 00 05 code_length
2a b7 00 01 b1 JVM定義的操作碼代碼段數組
00 00 exception_table_length
00 01 attributes_count 一個,接下來是attribute_info結構
00 0a attribute_name_index 看看constant_pool的第10項: LineNumberTable(顯然調試用)
00 00 00 06 attribute_length
00 01 line_number_table_length
00 00 start_pc
00 01 line_number

1. 00 09 access_flags PUBLIC & STATIC
00 0b name_index; 表明當前描述main函數
00 0c descriptor_index; ...([Ljava/lang/String;)V
00 01 attributes_count;
00 09 attribute_name_index Code
00 00 00 25 attribute_length = 37
00 02 max_stack
00 01 max_locals 
00 00 00 09 code_length
b2 00 02 12 03 b6 00 04 b1 代碼數組 codeArray1[0-8]
00 00 exception_table_length
00 01 attributes_count 接下來是attribute_info結構
00 0a attribute_name_index LineNumberTable
00 00 00 0a attribute_length
00 02 line_number_table_length
00 00 start_pc
00 05 line_number
00 08 start_pc : codeArray1[8] = 0xb1 --> return
00 06 line_number 第6行源程序爲 }


00 01 attributes_count
00 0d attribute_name_index 屬性爲SourceFile
00 00 00 02 attribute_length
00 0e sourcefile_index constant_pool[0x0e] --- > "Test.java"


接下來我們看看main()函數對應的代碼:

b2 00 02 12 03 b6 00 04 b1

0xb2 0x00 0x02 : getstatic #2
看看constant_pool[0x02] :09 00 10 00 11
0x09 : CONSTANT_Fieldref 0x10: class index 0x11 :name-type-index
constant_pool[0x10]: --> constant_pool[0x17] : java/lang/System
constant_pool[0x11]: 0x18 : class index 0x19 :name-type-index
constant_pool[0x18] : out
constant_pool[0x19] : Ljava/io/PrintStream

即 System.out 爲 java.io.PrintStream 類型



0x12 0x03 : ldc #3
看看 constant_pool[3] : 08 00 12 0x08 : CONSTANT_String 0x12 : string_index
指向一個字符串 constant_pool[0x12]: "Hello World!"
故該指令加載了字符串索引到本地棧
0xb6 0x00 0x04: invokevirtual #4 
------->到constant_pool查查 0x13 :class 0x14 :name-type
看看constant_pool[0x13]:java/io/PrintStream
constant_pool[20] :--> 00 1b 00 1c
constant_pool[0x1b]:println
. . . . :(Ljava/lang/String;)V

故該指令調用了 java.io.PrintStream.println(java.lang.String)
而參數則爲 ldc #3 加載的 "Hello World!"
0xb1 : return

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章