class文件分析 – JAVA編程語言程序開發技術文章

本文通過分析一個簡單java類文件的字節碼,希望借此能快速瞭解java類文件格式

為瞭分析字節碼,必須有一個整體的格式如下:

 

 

 

以上面的表作為分析的基礎,開始行動!

 

一段簡單的java代碼

 9 package org.kaka.clazz;
                                                                                                                                          
public class TestClass {
        private int m;
                                                                                                                                                   
        public int inc(){
                return m+1;
        }
}

 

查看字節碼

  20 00000000  ca fe ba be 00 00 00 32  00 13 0a 00 04 00 0f 09  |…….2……..|
00000010  00 03 00 10 07 00 11 07  00 12 01 00 01 6d 01 00  |………….m..|
00000020  01 49 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29  |.I…<init>…()|
00000030  56 01 00 04 43 6f 64 65  01 00 0f 4c 69 6e 65 4e  |V…Code…LineN|
00000040  75 6d 62 65 72 54 61 62  6c 65 01 00 03 69 6e 63  |umberTable…inc|
00000050  01 00 03 28 29 49 01 00  0a 53 6f 75 72 63 65 46  |…()I…SourceF|
00000060  69 6c 65 01 00 0e 54 65  73 74 43 6c 61 73 73 2e  |ile…TestClass.|
00000070  6a 61 76 61 0c 00 07 00  08 0c 00 05 00 06 01 00  |java…………|
00000080  18 6f 72 67 2f 6b 61 6b  61 2f 63 6c 61 7a 7a 2f  |.org/kaka/clazz/|
00000090  54 65 73 74 43 6c 61 73  73 01 00 10 6a 61 76 61  |TestClass…java|
000000a0  2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 00 21 00 03  |/lang/Object.!..|
000000b0  00 04 00 00 00 01 00 02  00 05 00 06 00 00 00 02  |…………….|
000000c0  00 01 00 07 00 08 00 01  00 09 00 00 00 1d 00 01  |…………….|
000000d0  00 01 00 00 00 05 2a b7  00 01 b1 00 00 00 01 00  |……*………|
000000e0  0a 00 00 00 06 00 01 00  00 00 03 00 01 00 0b 00  |…………….|
000000f0  0c 00 01 00 09 00 00 00  1f 00 02 00 01 00 00 00  |…………….|
00000100  07 2a b4 00 02 04 60 ac  00 00 00 01 00 0a 00 00  |.*….`………|
00000110  00 06 00 01 00 00 00 07  00 01 00 0d 00 00 00 02  |…………….|
00000120  00 0e                                             |..|
00000122

step 1) java magic number

首先的四個字節0x ca fe ba be 即class文件的magic number

 

 

step2) java version

接下來的四個字節0x 00 00 00 32 即class文件的版本號(可參看class文件的版本號列表)

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

00000000  ca fe ba be 00 00 00 32  00 13 0a 00 04 00 0f 09  |.......2........|

00000010  00 03 00 10 07 00 11 07  00 12 01 00 01 6d 01 00  |.............m..|

00000020  01 49 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29  |.I...<init>...()|

00000030  56 01 00 04 43 6f 64 65  01 00 0f 4c 69 6e 65 4e  |V...Code...LineN|

00000040  75 6d 62 65 72 54 61 62  6c 65 01 00 03 69 6e 63  |umberTable...inc|

00000050  01 00 03 28 29 49 01 00  0a 53 6f 75 72 63 65 46  |...()I...SourceF|

00000060  69 6c 65 01 00 0e 54 65  73 74 43 6c 61 73 73 2e  |ile...TestClass.|

00000070  6a 61 76 61 0c 00 07 00  08 0c 00 05 00 06 01 00  |java............|

00000080  18 6f 72 67 2f 6b 61 6b  61 2f 63 6c 61 7a 7a 2f  |.org/kaka/clazz/|

00000090  54 65 73 74 43 6c 61 73  73 01 00 10 6a 61 76 61  |TestClass...java|

000000a0  2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 00 21 00 03  |/lang/Object.!..|

000000b0  00 04 00 00 00 01 00 02  00 05 00 06 00 00 00 02  |................|

000000c0  00 01 00 07 00 08 00 01  00 09 00 00 00 1d 00 01  |................|

000000d0  00 01 00 00 00 05 2a b7  00 01 b1 00 00 00 01 00  |......*.........|

000000e0  0a 00 00 00 06 00 01 00  00 00 03 00 01 00 0b 00  |................|

000000f0  0c 00 01 00 09 00 00 00  1f 00 02 00 01 00 00 00  |................|

00000100  07 2a b4 00 02 04 60 ac  00 00 00 01 00 0a 00 00  |.*....`.........|

00000110  00 06 00 01 00 00 00 07  00 01 00 0d 00 00 00 02  |................|

00000120  00 0e                                             |..|

00000122

step3) 常量池

        接下來是描述常量表的長度0x 00 13,一共是(19-1)項,人肉分析如下

第1項

tag: 0x 0a ,CONSTANT_Methodref_info即方法聲明

index: 0x 00 04,指向常量池中CONSTANT_Class_info,見常量池第4項

index: 0x 00 0f,指向常量池中CONSTANT_NameAndType_info見常量池第15項

第2項

tag 0x 09,CONSTANT_Fieldref_info即字段聲明

index: 0x 00 03,指向常量池中CONSTANT_Class_info,見常量池第3項

index: 0x 00 10,指向常量池中CONSTANT_NameAndType_info見常量池第16項

第3項

tag:0x 07,CONSTANT_Class_info即類聲明

index: 0x 00 11,指向常量池中的CONSTANT_Utf8_info,第17項

第4項

tag:0x 07,CONSTANT_Class_info即類聲明

index: 0x 00 12,指向常量池中的CONSTANT_Utf8_info,第18項

第5項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 01 ,長度為1個字節

bytes: 0x 6d, 內容為"m"

第6項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 01 ,長度為1個字節

bytes: 0x 49, 內容為"I"

第7項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 06 ,長度為6個字節

bytes: 0x 3c 69 6e  69 74 3e, 內容為"<init>"

第8項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 03 ,長度為3個字節

bytes: 0x 28 29 56 內容為"()V"

第9項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 04 ,長度為4個字節

bytes: 0x 43 6f 64 65 內容為"Code"

第10項

tag:0x 01,CONSTANT_Utf8_info即字符串說明 

length: 0x 00 0f ,長度為15個字節

bytes: 0x 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62  6c 65內容為"LineNumberTable" 

第11項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 03,長度為3個字節

bytes: 0x 69 6e 63 內容為"inc"

第12項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 03,長度為3個字節

bytes: 0x 28 29 49 內容為"()I"

第13項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 0a,長度為10個字節

bytes: 0x 53 6f 75 72 63 65 46  69 6c 65內容為"SourceFile"

第14項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 04,長度為14個字節

bytes: 0x 54 65  73 74 43 6c 61 73 73 2e  6a 61 76 61內容為"TestClass.java"

第15項

tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法說明

index:0x 00 07 ,字段或者方法常量索引,見常量池第7項

index:0x 00 08,字段或者方法常量索引,見常量池第8項

第16項

tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法說明

index:0x 00 05 ,字段或者方法常量索引,見常量池第5項

index:0x 00 06,字段或者方法常量索引,見常量池第6項

第17項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 18,長度為24個字節

bytes: 0x 6f 72 67 2f 6b 61 6b  61 2f 63 6c 61 7a 7a 2f 54 65 73 74 43 6c 61 73  73內容為"org/kaka/clazz/TestClass"

第18項

tag:0x 01,CONSTANT_Utf8_info即字符串說明

length: 0x 00 10,長度為16個字節

bytes: 0x 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 內容為"java/lang/Object"

 

可以看出

CONSTANT_Methodref_info、CONSTANT_Fieldref_info都 依賴於CONSTANT_Class_info、CONSTANT_NameAndType_info

CONSTANT_Class_info依賴於CONSTANT_Utf8_info,後者用於聲明類的名字

CONSTANT_NameAndType_info依賴於兩個CONSTANT_Utf8_info

第一個CONSTANT_Utf8_info用於說明方法或者字段的名字

第一個CONSTANT_Utf8_info用於說明方法的入參以及返回類型或者字段的類型

列舉常量池結構

 

 

 

 

 

可以對比下javap的結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 Compiled from "TestClass.java"
public class org.kaka.clazz.TestClass extends java.lang.Object
  SourceFile: "TestClass.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method   #4.#15; //  java/lang/Object."<init>":()V
const #2 = Field    #3.#16; //  org/kaka/clazz/TestClass.m:I
const #3 = class    #17;    //  org/kaka/clazz/TestClass
const #4 = class    #18;    //  java/lang/Object
const #5 = Asciz    m;
const #6 = Asciz    I;
const #7 = Asciz    <init>;
const #8 = Asciz    ()V;
const #9 = Asciz    Code;
const #10 = Asciz   LineNumberTable;
const #11 = Asciz   inc;
const #12 = Asciz   ()I;
const #13 = Asciz   SourceFile;
const #14 = Asciz   TestClass.java;
const #15 = NameAndType #7:#8;//  "<init>":()V
const #16 = NameAndType #5:#6;//  m:I
const #17 = Asciz   org/kaka/clazz/TestClass;
const #18 = Asciz   java/lang/Object;
                                                                                          
{
public org.kaka.clazz.TestClass();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 3: 0
                                                                                          
                                                                                          
public int inc();
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   aload_0
   1:   getfield    #2; //Field m:I
   4:   iconst_1
   5:   iadd
   6:   ireturn
  LineNumberTable:
   line 7: 0
                                                                                          
                                                                                          
}

兩者是一致的

 

 

 

step4) 訪問標志

    接下來的兩個字節0x 00 21是由下標中的0x 00 01 | 0x 0020計算出來的,表類是public 的,且是jdk1.2以後的編譯器編譯出來的

 

 

 

訪問標志  標志名稱 標志值 含義
ACC_PUBLIC 0x0001 是否為public類型
ACC_FINAL 0x0010 是否被聲明為final,隻有類可以設置,接口不能設置該標志
ACC_SUPER 0x0020 是否允許使用invokespecial字節碼指令(查瞭一下該命令的作用為"調用超類的構造

方法,實例的構造方法,私有方法"),JDK1.2以後的編譯器編譯出來的class文件

該標志都為真
 
ACC_INTERFACE 0x0200 標識這是一個接口 
ACC_ABSTRACT 0x0400  是否被聲明為abstract類型,對於接口和抽象類來說此標志

為真,其他類為假
 
ACC_SYNTHETIC 0x1000  標識這個類並非由用戶代碼生成
ACC_ANNOTATION 0x2000  標識這是一個註解
ACC_ENUM 0x4000  標識這是一個枚舉

 

step5)  類信息

 

頭兩個字節0x 00 03,指向常量池中的第3項,即類名

接下來的兩個字節 0x 00 04 指向常量池中的第4項,即父類名

接下來的兩個字節 0x 00 00 表示類實現的接口的個數,本例子中為0,如果有的n個話,後面還會有2n個字節的常量池索引

step 6)字段信息

 頭兩個字節 0x 00 01 字段的個數,本例中為1

  接下來就是1個field_info結構

頭兩個字節 0x 00 02 表示accss_flags,即private

接下來0x 00 05表示字段名,指向常量池的第5項,即本例中"m"

接下來的0x 00 06 表示字段的描述信息,指向常量池的第6項,即本例中"I" ,表示是int類型

接下來的0x 00 00 表示字段的屬性信息(用於擴展或者補充說明字段信息)的個數,本例中為0,如果有n個的話,後面還會有n個attribute_info結構

step 7) 方法信息

頭兩個字節為0x 00 02,表示方法的個數,本例中為2

接下來是兩個method_info結構

第1個method_info結構

頭兩個字節0x 00 01 表示accss_flags,即public 方法

接下來0x 00 07表示方法名,指向常量池的第7項,即本例中"<init>"

接下來的0x 00 08 表示方法的描述信息,指向常量池的第8項,即本例中"()V" ,表示是一個沒有參數,返回類型為void的方法

接下來的0x 00 01表示方法的屬性信息的個數,本例中為1

接下是1個attribute_info結構

0x 00 09表示屬性名,指向常量池的第9項,即Code,表示方法的代碼

0x 00 00 00 1d 表示該屬性的長度,即29個字節

0x 00 01 表示max_stack

0x 00 01 表示max_locals

0x 00 00 00 05 表示code_length, 即該方法的代碼為編譯後為5個字節

0x 2a b7  00 01 b1 即代碼

0x 00 00 表示 沒有異常信息

0x 00 01 表示有一個屬性信息

接下是1個attribute_info結構

0x 00 0a 表示屬性名,指向常量池的第10項,即LineNumberTable,表示方法的行號

0x 00 00 00 06 表示該屬性的長度,即6個字節

0x 00 01 表示有1行

0x 00  00 表示字節碼為第0行

0x 00 03 表示上面的字節碼為第0行對應源碼中的第3行

第2個method_info結構

頭兩個字節00 01 表示accss_flags,即public 方法

接下來0x 00 0b表示方法名,指向常量池的第11項,即本例中"inc"

接下來的0x 00 0c 表示方法的描述信息,指向常量池的第12項,即本例中"()I" ,表示是一個沒有參數,返回類型為int的方法

接下來的0x 00 01表示方法的屬性信息的個數,本例中為1

接下是1個attribute_info結構

0x 00 09表示屬性名,指向常量池的第9項,即Code,表示方法的代碼

0x 00 00 00 1f 表示該屬性的長度,即31個字節

0x 00 02 表示max_stack

0x 00 01 表示max_locals

0x 00 00 00 07 表示code_length, 即該方法的代碼為編譯後為7個字節

0x 2a b4 00 02 04 60 ac 即代碼

0x 00 00 表示 沒有異常信息

0x 00 01 表示有一個屬性信息

接下是1個attribute_info結構

0x 00 0a 表示屬性名,指向常量池的第10項,即LineNumberTable,表示方法的行號

0x 00 00 00 06 表示該屬性的長度,即6個字節

0x 00 01 表示有1行

0x 00  00 表示字節碼為第0行

0x 00 07 表示上面的字節碼為第0行對應源碼中的第7行

step 8) 屬性信息

頭兩個字節為0x 00 01,表示有一個屬性

接下是1個attribute_info結構

0x 00 0d表示屬性名,指向常量池的第13項,即SourceFile,表示類的源文件

0x 00 00 00 02,屬性長度,即接下來的字節個數

0x 00 0e 表示源文件名,指向常量池的第14項,即"TestClass.java"

 

 

 

小結  

 

如果想分析類文件,大部分情況下按字節分析,直接使用javap即可,但如果知道字節碼也可以更好的瞭解javap的結果

幾個種要的結構需要瞭解,本文就不一一列舉具體信息(這個網上到處都是)

 

method_info

 

會包含attribute_info

 

filed_info

 

會包含attribute_info

 

attribute_info

 

共有好幾種attribute,本例中就用到瞭Code,LineNumberTable,SourceFile,每種不同的屬性結構都不一樣

 

特別Code類型的attribute_info,編譯過後的字令碼就存放在裡面

另外還有LineNumberTable以及本例中未提到的LocalVariableTable都是和調試息息相關的

 

有可能會嵌套attribute_info

發佈留言