<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-8  評(píng)論-67  文章-0  trackbacks-0
      從上一篇博客(Java Class文件解析)的分析可以看出,Class文件中的各項(xiàng)是按照一定的包含關(guān)系和次序關(guān)系存儲(chǔ)的,因此Class文件可以從頭到尾地被解析為各個(gè)項(xiàng)。下面請(qǐng)看一個(gè)解析實(shí)例:

    這里我們以非常著名的HelloWorld為實(shí)例來(lái)分析Java Class文件。HelloWorld的源代碼如下:

    package bytecodeResearch;

    public class HelloWorld {

        
    //定義兩個(gè)靜態(tài)變量
        private static String str_1 = "Hello";
        
    private static String str_2 = "World";
        
        
    /**
         * 靜態(tài)方法
         * 
    @param str
         
    */

        
    private static void Hello(String str)
        
    {
            System.out.println(str);
        }
        
        
    /**
         * 靜態(tài)方法
         * 
    @param str
         
    */

        
    private static void World(String str)
        
    {
            System.out.println(str);
        }

        
        
    /**
         * 程序入口方法
         * 
    @param args
         
    */

        
    public static void main(String[] args)
        
    {
            Hello(str_1);
            World(str_2);
        }

    }

        編譯單元HelloWorld.java文件經(jīng)過編譯器編譯之后,將得到一個(gè)HelloWorld.class文件。需要說(shuō)明的是,經(jīng)過不同的編譯器編譯之后得到的HelloWorld.class文件可能不一樣,本人選用的開發(fā)工具是Eclipse3.3-europa版本,Eclipse SDK自帶的JDT工具內(nèi)置了增量式Java編譯器,這個(gè)編譯器與javac完全兼容。本人以下的分析都是基于Eclipse自帶的編譯器編譯得到的Class文件,如果有人用了Jikes編譯器、GNU的編譯器或者其它版本的javac編譯器的話,得到的Class文件跟我的可能不完全一樣的話,但是Class文件的格式肯定是一樣的,因此分析的原理也都是一樣的,即都是基于上一篇博客(Java Class文件解析)給出的ClassFile結(jié)構(gòu)圖示。

        經(jīng)過Eclipse3.3編譯得到的Class文件內(nèi)容如下

      

     

    HelloWorld.class文件的內(nèi)容

    00000000h: CA FE BA BE 00 00 00 31  00 31 07 00 02  01 00 1B (該類共有48個(gè)常量池表項(xiàng))

    00000010h: 62 79 74 65 63 6F 64 65 52 65 73 65 61 72 63 68 

    00000020h: 2F 48 65 6C 6C 6F 57 6F 72 6C 64  07 00 04 01 00 

    00000030h: 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 

    00000040h: 74 01 00 05 73 74 72 5F 31 01 00 12 4C 6A 61 76 

    00000050h: 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B  01 00  

    00000060h: 05 73 74 72 5F 32 01 00 08 3C 63 6C 69 6E 69 74 

    00000070h: 3E  01 00 03 28 29 56  01 00 04 43 6F 64 65  08 00   (10號(hào)常量池表項(xiàng))

    00000080h: 0C  01 00 05 48 65 6C 6C 6F  09 00 01 00 0E 0C 00 

    00000090h: 05 00 06 08 00 10  01 00 05 57 6F 72 6C 64 09 00  

    000000a0h: 01 00 12 0C 00 07 00 06 01 00 0F 4C 69 6E 65 4E 

    000000b0h: 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63  

    000000c0h: 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01   (20號(hào)常量池表項(xiàng))

    000000d0h: 00 06 3C 69 6E 69 74  3E  0A 00 03 00 17  0C 00 15 

    000000e0h: 00 09 01 00 04 74 68 69 73 01 00 1D 4C 62 79 74 

    000000f0h: 65 63 6F 64 65 52 65 73 65 61 72 63 68 2F 48 65 

    00000100h: 6C 6C 6F 57 6F 72 6C 64 3B  01 00 15 28 4C 6A 61  

    00000110h: 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29  

    00000120h: 56  09 00 1C 00 1E 07 00 1D 01 00 10 6A 61 76 61 

    00000130h: 2F 6C 61 6E 67 2F 53 79 73 74 65 6D  0C 00 1F 00   (30號(hào)常量池表項(xiàng))

    00000140h: 20 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F  

    00000150h: 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 0A 

    00000160h: 00 22 00 24 07 00 23 01 00 13 6A 61 76 61 2F 69 

    00000170h: 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D  0C 00 25  

    00000180h: 00 1A 01 00 07 70 72 69 6E 74 6C 6E 01 00 03 73 

    00000190h: 74 72  01 00 04 6D 61 69 6E 01 00 16  28 5B 4C 6A  

    000001a0h: 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B   (40號(hào)常量池表項(xiàng))

    000001b0h: 29 56  0A 00 01 00 2A 0C 00 0C 00 1A 0A 00 01 00 

    000001c0h: 2C  0C 00 10 00 1A 01 00 04 61 72 67 73 01 00 13 

    000001d0h: 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 

    000001e0h: 6E 67 3B 01 00 0A 53 6F 75 72 63 65 46 69 6C 65  

    000001f0h: 01 00 0F 48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61  

    00000200h: 76 61  00 21 00 01 00 03 00 00  00 02 00 0A 00 05    (該類有0x0002個(gè)field)

    00000210h: 00 06  00 00  00 0A 00 07 00 06 00 00 00 05  00 08    (該類有0x0005個(gè)method)

    00000220h: 00 08 00 09 00 01  00 0A 00 00 00 33 00 01 00 00        (1個(gè)method_info)

    00000230h: 00 00 00 0B 12 0B B3 00 0D 12 0F B3 00 11 B1 00 

    00000240h: 00 00 02 00 13 00 00 00 0E 00 03 00 00 00 06 00 

    00000250h: 05 00 07 00 0A 00 03 00 14 00 00 00 02 00 00 00        (2個(gè)method_info)

    00000260h: 01  00 15 00 09  00 01  00 0A  00 00 00 2F 00 01 00 

    00000270h: 01 00 00 00 05 2A B7 00 16 B1 00 00 00 02 00 13 

    00000280h: 00 00 00 06 00 01 00 00 00 03 00 14 00 00 00 0C  

    00000290h: 00 01 00 00 00 05 00 18 00 19 00 00  00 0A 00 0C    (3個(gè)method_info)

    000002a0h: 00 1A  00 01  00 0A 00 00 00 36  00 02 00 01 00 00 

    000002b0h: 00 08 B2 00 1B 2A B6 00 21 B1 00 00 00 02 00 13 

    000002c0h: 00 00 00 0A 00 02 00 00 00 0F 00 07 00 10 00 14  

    000002d0h: 00 00 00 0C 00 01 00 00 00 08 00 26 00 06 00 00  

    000002e0h: 00 0A 00 10 00 1A 00 01 00 0A 00 00 00 36 00 02     (4個(gè)method_info)

    000002f0h: 00 01 00 00 00 08 B2 00 1B 2A B6 00 21 B1 00 00  

    00000300h: 00 02 00 13 00 00 00 0A 00 02 00 00 00 18 00 07 

    00000310h: 00 19 00 14 00 00 00 0C 00 01 00 00 00 08 00 26  

    00000320h: 00 06  00 00 00 09 00 27 00 28 00 01 00 0A 00 00    (5個(gè)method_info)

    00000330h: 00 3F  00 01 00 01 00 00 00 0D B2 00 0D B8 00 29 

    00000340h: B2 00 11 B8 00 2B B1 00 00 00 02 00 13 00 00 00 

    00000350h: 0E 00 03 00 00 00 21 00 06 00 22 00 0C 00 23 00 

    00000360h: 14 00 00 00 0C 00 01 00 00 00 0D 00 2D 00 2E 00 

    00000370h: 00 00 01 00 2F 00 00 00 02 00 30                              (該類有0x0001個(gè)attribute)

     

     

         注:該Class的內(nèi)容是經(jīng)過解析的,即將.class文件的二進(jìn)制字節(jié)流轉(zhuǎn)換為16進(jìn)制的數(shù)字字符流形式。由于UE查看Class文件時(shí),用一個(gè)8位的16進(jìn)制數(shù)來(lái)表示每一行起始字節(jié)對(duì)應(yīng)于Class文件中的字節(jié)號(hào),為了不造成視覺差異,本人寫了一段程序來(lái)讀寫Class文件內(nèi)容,其輸出結(jié)果就是上面這段內(nèi)容,每16個(gè)字節(jié)占一行,每個(gè)字節(jié)均拆解成216進(jìn)制整數(shù)字符,每一行左端的8位的16進(jìn)制整數(shù)就表示該行的起始字節(jié)對(duì)應(yīng)于原Class文件中的字節(jié)號(hào)。這個(gè)程序貼在了我第一篇blog里--個(gè)讀取Class文件的示例程序。

        好了,閑言少敘!下面開始正式解析這個(gè)Class文件。

     

    按照上一篇博客(Java Class文件解析)介紹的ClassFile結(jié)構(gòu)圖示以及對(duì)ClassFile結(jié)構(gòu)的詳細(xì)分析,可以將HelloWorld.class文件的各個(gè)項(xiàng)順序地解析如下:

    (1) 4個(gè)字節(jié)0xCAFEBABEmagic項(xiàng)的內(nèi)容。

    (2) 接下來(lái)的2個(gè)字節(jié)0x0000minor_version項(xiàng)的內(nèi)容,即該Class文件的次版本號(hào)0。

    (3) 接下來(lái)的2個(gè)字節(jié)0x0031major_version項(xiàng)的內(nèi)容,即該Class文件的主版本號(hào)為49。這個(gè)主版本號(hào)對(duì)應(yīng)于J2SE5.0的編譯器的編譯結(jié)果。如果你用J2SE6.0的編譯器來(lái)編譯此HelloWorld.java程序的話,得到的主版本號(hào)應(yīng)該是0x0032,即50

    (4) 接下來(lái)的2個(gè)字節(jié)0x0031constant_pool_count項(xiàng)的內(nèi)容,它表示接下來(lái)共有連續(xù)的49-1=48個(gè)constant_pool表項(xiàng)。

    (5) 緊接著constant_pool_count項(xiàng)后面的是常量池列表項(xiàng)。常量池列表項(xiàng)的長(zhǎng)度是可變的,這是因?yàn)椴煌某A砍乇眄?xiàng)的格式是不一樣的。

    5.1)先來(lái)分析第一個(gè)常量池表項(xiàng)。上一篇博客(Java Class文件解析)中提到,每個(gè)常量池表項(xiàng)的具體格式是要根據(jù)其tag項(xiàng)(即該常量池表項(xiàng)的第一個(gè)字節(jié))來(lái)決定。因此,constant_pool_count項(xiàng)后面的第一字節(jié)就是第一個(gè)常量池表項(xiàng)的tag項(xiàng)的內(nèi)容,在這里為0x07cp_type表可知,tag值為0x07的對(duì)應(yīng)CONSTANT_Class結(jié)構(gòu)的常量池表項(xiàng)。再查閱CONSTANT_Class_info表,該表結(jié)構(gòu)如下:

    CONSTANT_Class_info {

                u1 tag;

                u2 name_index;

    }

           根據(jù)《JVM Spec(2nded)中對(duì)此表的說(shuō)明可知,1個(gè)字節(jié)的tag項(xiàng)就是剛才讀取的值0x07,而后的2個(gè)字節(jié)的name_index項(xiàng)表示對(duì)一個(gè)CONSTANT_Utf8_info表的索引,該索引項(xiàng)包含了類或者接口的完全限定名稱。對(duì)于HelloWorld.class這個(gè)類來(lái)說(shuō),第一個(gè)常量池表項(xiàng)就是一個(gè)CONSTANT_Class_info表的結(jié)構(gòu),其tag項(xiàng)的值是0x07,其name_index項(xiàng)的值是0x0002,即該常量池表項(xiàng)指向索引為0x0002的常量池表項(xiàng)。

    5.2)再來(lái)分析第二個(gè)常量池表項(xiàng)。其tag項(xiàng)值為0x01,查閱cp_type表可知,該常量池表項(xiàng)是一個(gè)CONSTANT_ Utf8_info表的結(jié)構(gòu),然后我們查閱CONSTANT_ Utf8_info表,該表結(jié)構(gòu)如下:

    CONSTANT_Utf8_info {
             u1 tag;
             u2 length;
             u1 bytes[length];
     }

           根據(jù)《JVM Spec(2nded)中對(duì)此表的說(shuō)明可知,1個(gè)字節(jié)的tag項(xiàng)的值就是0x012個(gè)字節(jié)的length項(xiàng)給出了后續(xù)的bytes數(shù)組的長(zhǎng)度(字節(jié)數(shù)),在這里為0x001B,即其后續(xù)的27個(gè)字節(jié)均是該bytes數(shù)組的內(nèi)容,它包含了按照變體UTF-8格式存儲(chǔ)(不是標(biāo)準(zhǔn)的UTF-8格式啊,具體的差別請(qǐng)查閱那兩個(gè)參考資料,都有說(shuō)明)的字符串中的字符。按照此變體格式可以將這27個(gè)字節(jié)解析為“bytecodeResearch/HelloWorld”,這27個(gè)字符實(shí)際上就是該ClassFile的完全限定名稱,這是為什么呢?因?yàn)榈谝粋€(gè)常量池表項(xiàng)是CONSTANT_Class_info結(jié)構(gòu)的,其name_index項(xiàng)指向的常量池表項(xiàng)包含了類或者接口的完全限定名稱,第二個(gè)常量池表項(xiàng)正是第一個(gè)常量池表項(xiàng)中name_index項(xiàng)所指向的常量池表項(xiàng),因此“bytecodeResearch/HelloWorld”這27個(gè)字符就是該ClassFile的完全限定名稱(注:是完全限定名稱的內(nèi)部形式,下同)

           5.3)再來(lái)分析第三個(gè)常量池表項(xiàng)。第二個(gè)常量池表項(xiàng)后的第一個(gè)字節(jié)為0x07,由cp_type表可知,tag值為0x07的對(duì)應(yīng)CONSTANT_Class結(jié)構(gòu)的常量池表項(xiàng)。類似于對(duì)第一個(gè)常量池表項(xiàng)的分析,0x07后面的兩個(gè)字節(jié)為name_index項(xiàng),其值為0x0004,即該常量池表項(xiàng)指向索引為0x0004的常量池表項(xiàng),且該常量池是CONSTANT_Utf8_info表的結(jié)構(gòu)

           5.4)下面來(lái)分析第四個(gè)常量池表項(xiàng)。該常量池表項(xiàng)的tag項(xiàng)值又是0x01,類似地,同第二個(gè)常量池表項(xiàng)的分析,該常量池表項(xiàng)也是一個(gè)CONSTANT_ Utf8_info表的結(jié)構(gòu),其length項(xiàng)值為0x0010,即其后續(xù)的bytes數(shù)組的長(zhǎng)度為0x0010個(gè)字節(jié)。按照《JVM Spec(2nded)中關(guān)于變體UTF-8格式的定義,可以將這16個(gè)字節(jié)解析為“java/lang/Object”,這是Java類層次結(jié)構(gòu)的根類Object類的完全限定名稱。

           類似地,可以分析第5,67,8,910都是一個(gè)CONSTANT_ Utf8_info結(jié)構(gòu),分別表示“str_1”,“Ljava/lang/String”,“str_2”,“<clinit>”,“()V”,“Code

           5.5…….其他常量池表項(xiàng)的分析原理是類似的,這里就不贅述了。

           5.6)第四十八個(gè)常量池表項(xiàng)的解析。經(jīng)過分析,第48個(gè)常量池起始于第000001f0h字節(jié),終止于第00000202h字節(jié)。該常量池表項(xiàng)又是一個(gè)CONSTANT_Utf8_info表的結(jié)構(gòu),其15字節(jié)的bytes數(shù)組項(xiàng)的值可解析為“HelloWorld.java”。由后面的分析說(shuō)明該常量池表項(xiàng)存儲(chǔ)的是該類文件的SourceFile屬性的信息。

           好了,該Class文件的常量池部分已經(jīng)解析結(jié)束了!下面開始其它部分的解析:

           (6)   在常量池列表項(xiàng)后面的兩個(gè)字節(jié)是該Java類型的access_flags項(xiàng),這里為0x0021。根據(jù)access_flags表可以查到,該值是0x00200x0001兩者的和,即該類的修飾符為ACC_PUBLIC+ACC_SUPER,前者表示該類是public類型,后者表示采用invokespecial指令特殊處理對(duì)超類的調(diào)用。具體可以查閱兩本參考資料中關(guān)于JVM指令集的描述J

           7)接下來(lái)的兩個(gè)字節(jié)是this_class項(xiàng),它是一個(gè)對(duì)常量池表項(xiàng)的索引,在這里值為0x0001,即它指向1號(hào)常量池表項(xiàng),而1號(hào)常量池表項(xiàng)是一個(gè)CONSTANT_Class_info結(jié)構(gòu),它指向2號(hào)常量池表項(xiàng),2號(hào)常量池表項(xiàng)的值為bytecodeResearch/HelloWorld,前面提到這是該Class文件的完全路徑名稱的內(nèi)部形式,因此this_class即指bytecodeResearch/HelloWorld

           (8) 接下來(lái)的兩個(gè)字節(jié)是super_class項(xiàng),它是一個(gè)對(duì)常量池表項(xiàng)的索引,在這里值為0x0003, 即它指向3號(hào)常量池表項(xiàng),查一下上面對(duì)3號(hào)常量池表項(xiàng)的分析,它指向4號(hào)常量池表項(xiàng),而4號(hào)常量池表項(xiàng)包含的值為“java/lang/Object”,即super_class的實(shí)際值為“java/lang/Object”。說(shuō)明我們分析的這個(gè)Class文件的超類是java.lang.Object。

    (9) 下面的兩個(gè)字節(jié)是interfaces_count項(xiàng),在這里的值為0x0000,這表示由該類直接實(shí)現(xiàn)或者由該接口所擴(kuò)展的超接口的數(shù)量為0,因此該Class文件中的interfaces列表項(xiàng)也就不存在了。

    (10)接下來(lái)的字節(jié)應(yīng)該是field項(xiàng)的內(nèi)容了。首先的兩個(gè)字節(jié)是fields_count項(xiàng),這里的值為0x0002,即該類聲明了兩個(gè)字段(變量),亦即該項(xiàng)之后的fields列表項(xiàng)的元素個(gè)數(shù)為2。由于fields列表項(xiàng)的類型為field_info,所以在fields_count項(xiàng)下面的字節(jié)是兩個(gè)連續(xù)的field_info結(jié)構(gòu),下面來(lái)詳細(xì)分析這兩個(gè)具體的field_info結(jié)構(gòu);

    10.1)第一個(gè)field_info,即第一個(gè)字段的相關(guān)信息。

    10.1.1)首先的兩個(gè)字節(jié)是第一個(gè)fieldaccess_flags項(xiàng),在這里的值為0x000A,查閱field_access_flags表可知該access_flags項(xiàng)表示的是ACC_PRIVATE+ACC_STATIC,即該字段是由privatestatic修飾的。

    10.1.2)接下來(lái)的兩個(gè)字節(jié)是name_index項(xiàng),在這里的值為0x0005,即該字段的簡(jiǎn)單名稱由第5個(gè)常量池表項(xiàng)描述的,根據(jù)上一篇博客(Java Class文件解析)的分析可知,該常量池包含的信息為str_1,即該字段的名稱為str_1。

    10.1.3)接下來(lái)的兩個(gè)字節(jié)是descriptor_index項(xiàng),在這里的值為0x0006,即該字段的描述符存儲(chǔ)在6個(gè)常量池表項(xiàng),根據(jù)上一篇博客(Java Class文件解析)的分析可知,這個(gè)字段的類型為“Ljava/lang/String”。在Class文件中,“L<classname>”表示一個(gè)類的實(shí)例,其中<classname>是這個(gè)內(nèi)部形式的完全限定類名。

    10.1.4)接下來(lái)的兩個(gè)字節(jié)是attributes_count項(xiàng),在這里的值為0x0000,即該字段沒有附加的屬性列表。因而也就不用討論attributes[]項(xiàng)了。

    10.2)第二個(gè)field_info,即第二個(gè)字段的相關(guān)信息。類似地,參照第一個(gè)字段信息的分析,我們很快就可以知道該字段的access_flags項(xiàng)為ACC_PRIVATE+ACC_STATIC,名字為str_2,類型描述符為“Ljava/lang/String”,attributes_count項(xiàng)的值為0x0000

    (11)接下來(lái)的字節(jié)應(yīng)該是method項(xiàng)的內(nèi)容了。首先的兩個(gè)字節(jié)是methods_count項(xiàng),這里的值為0x0005,即該類聲明了5個(gè)方法,亦即該項(xiàng)之后的methods列表項(xiàng)的元素個(gè)數(shù)為5。由于methods列表項(xiàng)的類型為method_info,所以在methods_count項(xiàng)下面的字節(jié)是5個(gè)連續(xù)的method_info結(jié)構(gòu),下面來(lái)詳細(xì)分析這5個(gè)具體的method_info結(jié)構(gòu):

    11.1)第1個(gè)method_info結(jié)構(gòu),即第一個(gè)方法的相關(guān)信息,如方法名、描述符(即方法的返回值及參數(shù)類型)以及一些其它信息。根據(jù)method_info表分析接下來(lái)的字節(jié)碼可以得到:

    11.1.1access_flags項(xiàng),值為0x0008,即給方法的訪問修飾符為ACC_STATIC,它表示這是一個(gè)static方法。

    11.1.2name_index項(xiàng),值為0x0008,8號(hào)常量池表項(xiàng)存儲(chǔ)的信息為<clinit>即該方法的名稱為<clinit>。這是一個(gè)類與接口初始化方法,這個(gè)方法是由Java編譯器編譯源代碼的時(shí)候產(chǎn)生的,Java編譯器將該類的所有類變量初始化語(yǔ)句和所有類型的靜態(tài)初始化器收集到一起,放到<clinit>方法中,該方法只能被JVM隱式地調(diào)用,專門用于把類型的靜態(tài)變量設(shè)置為它們正確的初始值。

    11.1.3descriptor_index項(xiàng),值為0x0009,9號(hào)常量池表項(xiàng)存儲(chǔ)的信息為()V,這表示該方法的沒有參數(shù),返回值為void

    11.1.4attributes_count項(xiàng),值為0x0001,即該方法有一個(gè)屬性。查閱屬性信息表的結(jié)構(gòu),如下所示:

    attribute_info {
             u2 attribute_name_index;
             u4 attribute_length;
             u1 info[attribute_length];
        }

    由這個(gè)表,我們可以知道attributes_count項(xiàng)后面的是這個(gè)屬性的attribute_name_index項(xiàng),該項(xiàng)的值為0x000A,該屬性的名字信息存儲(chǔ)在第10號(hào)常量池表項(xiàng)里。查閱第10號(hào)常量池表項(xiàng)可知,該屬性的名字為“Code”,然后我們查閱Code_attribute表,結(jié)構(gòu)如下:

    Code_attribute {
             u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }

    11.1.5Code_attribute項(xiàng),該項(xiàng)包含了一個(gè)Java方法,或者實(shí)例初始化方法,或者類或接口初始化方法的JVM指令和輔助信息。每個(gè)JVM的實(shí)現(xiàn)都必須要識(shí)別Code屬性,在每個(gè)method_info結(jié)構(gòu)也必須確切地有一個(gè)Code屬性。下面來(lái)具體分析這個(gè)屬性;

    11.1.5.1) attribute_name_index項(xiàng),2個(gè)字節(jié),該項(xiàng)的值為0x000A,查閱第10號(hào)常量池表型包含的信息后知該屬性的名字為“Code”。

    11.1.5.2attribute_length項(xiàng),4個(gè)字節(jié),值為0x00000033,這說(shuō)明該屬性的長(zhǎng)度,出去初始的6個(gè)字節(jié),還有0x33=51個(gè)字節(jié)。如果不愿意討論接下去的51字節(jié)的話,可以直接跳過這51字節(jié)(00000226h-0000025eh字節(jié)),討論下一個(gè)方法。

    11.1.5.3max_stack項(xiàng),2個(gè)字節(jié),值為0x0001,這表示該方法執(zhí)行中任何點(diǎn)操作數(shù)棧上字的最大個(gè)數(shù)為1。

    11.1.5.4max_locals項(xiàng),2個(gè)字節(jié),值為0x0000,這表示該方法使用的局部變量個(gè)數(shù)為0。

    11.1.5.5code_length項(xiàng),4個(gè)字節(jié),值為0x0000000B,這表示該方法的code數(shù)組中字節(jié)的總個(gè)數(shù)為11

    11.1.5.6code[]項(xiàng),由11.1.5.5)知,該方法的code數(shù)組共占11個(gè)字節(jié)。該code[]項(xiàng)給出了實(shí)現(xiàn)該方法的JVM代碼的實(shí)際字節(jié)。例如第一個(gè)指令是0x12,這是ldc指令,這個(gè)指令表示將一個(gè)常量池表項(xiàng)壓入棧,它需要一個(gè)操作數(shù),而它后面的一個(gè)字節(jié)是0x0B,因此這條指令加上其操作數(shù)就表示將常量池中的第0x0B號(hào)表項(xiàng)壓入棧。接下來(lái)的一個(gè)指令是0xB3,這是putstatic指令,這條指令表示設(shè)置類中靜態(tài)變量的值。它需要兩個(gè)操作數(shù)indexbyte1indexbyte2,這兩個(gè)操作數(shù)均占一個(gè)字節(jié),JVM執(zhí)行putstatic執(zhí)行時(shí),會(huì)通過計(jì)算(indexbyte1<<8)|indexbyte2生成一個(gè)對(duì)常量池表項(xiàng)的索引,這里的參數(shù)為0x000x0D,運(yùn)算結(jié)果是0x0D,因此這條指令的意思就是將操作數(shù)棧的當(dāng)前棧頂元素賦值給0x0D號(hào)常量池表項(xiàng)所存儲(chǔ)的字段(str_1),即完成對(duì)字段str_1的賦值。。同樣,下面的五個(gè)字節(jié)的意思,就是將索引為0x0F的常量池表項(xiàng)壓入操作數(shù)棧,并賦值給(0x00<<)|0x11=0x11號(hào)常量池表項(xiàng)中所存儲(chǔ)的字段(str_2),即完成對(duì)字段str_2的賦值。該Code數(shù)組的最后一個(gè)字節(jié)是0xB1,這是一條不帶操作數(shù)的指令return,它表示從方法中返回,返回值為void

    11.1.5.7exception_table_length項(xiàng),2個(gè)字節(jié),值為0x0000,這表示該方法的異常處理器的個(gè)數(shù)為0。因此exception_table[ ]就沒有必要討論了。

    11.1.5.8attributes_count項(xiàng),2個(gè)字節(jié),值為0x0002,這表示該方法Code屬性具有兩個(gè)屬性。當(dāng)前由Code屬性定義和使用的兩個(gè)屬性是LineNumberTaleLocalVariableTable屬性。

    11.1.5.9attributes[ ]項(xiàng),由于LineNumberTaleLocalVariableTable兩個(gè)屬性都包含了一些調(diào)試信息,但是兩者都是可選屬性,因此這里就不多討論了。

    11.2)第2個(gè)method_info結(jié)構(gòu),即第2個(gè)方法的相關(guān)信息。第2個(gè)方法是實(shí)例初始化方法<init>,這段方法在Class文件中的字節(jié)編號(hào)為:0000025fh-0000029bh字節(jié),感興趣的朋友請(qǐng)繼續(xù)分析下去,原理和第一個(gè)方法的分析是一樣的。

    11.3)第3個(gè)method_info結(jié)構(gòu),即第3個(gè)方法的相關(guān)信息。第3個(gè)方法是該類的靜態(tài)方法Hello,這段方法在Class文件中的字節(jié)編號(hào)為:0000029ch-000002dfh字節(jié),感興趣的朋友請(qǐng)繼續(xù)分析下去,原理和第一個(gè)方法的分析是一樣的。

    11.4)第4個(gè)method_info結(jié)構(gòu),即第4個(gè)方法的相關(guān)信息。第4個(gè)方法是該類的靜態(tài)方法World,這段方法在Class文件中的字節(jié)編號(hào)為:000002e0h-00000323h字節(jié),感興趣的朋友請(qǐng)繼續(xù)分析下去,原理和第一個(gè)方法的分析是一樣的。

    11.5)第5個(gè)method_info結(jié)構(gòu),即第5個(gè)方法的相關(guān)信息。第5個(gè)方法是該類文件的入口main方法,這段方法在Class文件中的字節(jié)編號(hào)為:00000324h-00000370h字節(jié),感興趣的朋友請(qǐng)繼續(xù)分析下去,原理和第一個(gè)方法的分析是一樣的。

    (12) attributes_count項(xiàng),2個(gè)字節(jié),該ClassFile的屬性計(jì)數(shù)項(xiàng),它的值為0x0001,表示在后續(xù)的attributes列表中的attributes_info表的總個(gè)數(shù)為1

    (13) attributes[ ]項(xiàng),該ClassFile的屬性列表項(xiàng),這是Class文件的最后一項(xiàng)了!由(12)知,該列表項(xiàng)只有一個(gè)表項(xiàng)。由attribute_info表結(jié)構(gòu)

    attribute_info {
                       u2 attribute_name_index;
                       u4 attribute_length;
                       u1 info[attribute_length];
        }

    可知,attributes_count項(xiàng)后面的兩個(gè)字節(jié)是attribute_name_index項(xiàng),它的值為0x002F,它表示對(duì)常量池編號(hào)為0x002F的表項(xiàng)的一個(gè)索引。這個(gè)索引表項(xiàng)存儲(chǔ)的信息為”SourceFile”,即該ClassFile屬性的名稱為SourceFile,該屬性是一個(gè)可選的定長(zhǎng)屬性,對(duì)于給定的ClassFile結(jié)構(gòu)的attributes列表中不能有多于一個(gè)的SourceFile屬性;查閱SourceFile_attribute表可知,下面的4個(gè)字節(jié)為attribute_length項(xiàng),其值為0x00000002,它表示在該項(xiàng)后面還有2個(gè)字節(jié)的信息。根據(jù)SourceFile_attribute表,最后的這兩個(gè)字節(jié)是sourcefile_index項(xiàng),該項(xiàng)的值是一個(gè)對(duì)CONSTANT_Utf8_info結(jié)構(gòu)的常量池表項(xiàng)的索引,其信息表示的是該Class文件的源文件名稱。在這里值為0x0030,根據(jù)上一篇博客(Java Class文件解析)的分析,第48號(hào)常量池表項(xiàng)存儲(chǔ)的信息可解析為“HelloWorld.java”,這是該Class文件的源文件名稱(不包括路徑)

           好了,到此為止,該Class文件的實(shí)例已經(jīng)全部解析完畢,大功告成:)


     

    posted on 2008-02-03 13:03 獨(dú)孤求敗 閱讀(4863) 評(píng)論(33)  編輯  收藏 所屬分類: Java ByteCode

    評(píng)論:
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 13:55 | 小白鼠兒
    很暈很深?yuàn)W  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 14:14 | dennis
    很好的文章,有興趣研究這個(gè)的都會(huì)自己去找spec或者《inside jvm》
    不過現(xiàn)在能沉下心來(lái)讀你這篇文章的,我怕沒幾個(gè)  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 15:22 | 久城
    很暈很迷糊......

    看來(lái)我還需要看很多東西才能消化它.....  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 21:11 | 獨(dú)孤求敗
    @dennis

    呵呵...當(dāng)然,我也是參考了這兩本書,在上一篇blog里已經(jīng)說(shuō)了,我只是整理了一下,并且加進(jìn)去一些自己的理解而已:)
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 21:12 | 獨(dú)孤求敗
    @小白鼠兒

    找那兩本參考資料看看:)  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-03 21:12 | 獨(dú)孤求敗
    @久城

    找那兩本參考資料看看:)  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-12 14:43 | loocky
    值得研究一下看看,我相信不超過1%的人能都讀完  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-14 14:18 | zhyiwww
    我覺得,解析了class文件,然后能更深入的引導(dǎo)我們?nèi)ダ斫饽切〇|西,是值得我們?nèi)ド钊胨伎嫉膯栴}。  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-15 02:14 | 獨(dú)孤求敗
    呵呵...真正需要的人會(huì)看完的:)
    @loocky
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-15 02:16 | 獨(dú)孤求敗
    你說(shuō)得很對(duì),這只是入門級(jí)的技術(shù)blog
    @zhyiwww
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例[未登錄] 2008-02-15 10:00 | 屹礫
    如果要做JVM或者研究JVM原理的話,做這個(gè)研究還是蠻有意義的。  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-15 10:11 | 獨(dú)孤求敗
    贊同啊~@屹礫
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-02-17 12:36 | 83bbb
    有點(diǎn)意思,  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-21 21:33 | 王躍峰
    我就是仔細(xì)研究這個(gè)文章的人?。?
    很好,很有用。
    我最近也在解析class文件,就是不知道怎樣獲取類中的方法的參數(shù)名字。  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-25 17:06 | 王躍峰
    不知道博主還在寫嗎?我研究了下,寫了程序要獲取到class中的method的參數(shù)名稱,目前只能得到方法描述.
    一般是這樣的
    create(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    我現(xiàn)在想知道參數(shù)的名字啊,常量池中有這個(gè)名字的
    怎樣通過method_info聯(lián)系到哪個(gè)參數(shù)名稱?
    求老大賜教.mail:wang.yue.feng@163.com   回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-29 15:15 | 獨(dú)孤求敗
    可以查閱Java System API
    能解決的,Method,MethodDescriptor,ParameterDescriptor這三個(gè)類提供的方法應(yīng)該就能解決你的問題:)
    @王躍峰
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-30 12:30 | 王躍峰
    謝謝大哥幫忙啊.

    我試了下,好象也不行啊,ParameterDescriptor類里面根本沒有表示參數(shù)名字的屬性和方法.

    另外,我也嘗試用了兩個(gè)分析class的工具jcfe和classediter,確實(shí)最后是按照classfile的文件結(jié)構(gòu)解析的.但是在method_info中沒有參數(shù)名稱的跡象啊.參數(shù)類型這個(gè)是很明顯的.

    求教方法和它的參數(shù)是怎么對(duì)應(yīng)的?

    最后,考慮method_info中的code屬性,這個(gè)里面不知能得到什么東西?  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-30 16:27 | 王躍峰
    import java.beans.MethodDescriptor;
    import java.beans.ParameterDescriptor;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.Method;
    public class FileClassLoader extends ClassLoader {
    public static final String drive = "d:/";
    public static final String fileType = ".class";

    public FileClassLoader() {
    super();
    }
    public FileClassLoader(ClassLoader arg0) {
    super(arg0);
    }
    public Class findClass(String name) {
    byte[] data = loadClassData(name);
    return defineClass(name, data, 0, data.length);
    }
    public byte[] loadClassData(String name) {
    FileInputStream fis = null;
    byte[] data = null;
    try {
    fis = new FileInputStream(new File(drive + name + fileType));

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    int ch = 0;
    while ((ch = fis.read()) != -1) {
    baos.write(ch);
    }
    data = baos.toByteArray();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return data;
    }
    public static void main(String[] args) throws Exception {
    FileClassLoader loader = new FileClassLoader();
    Class objClass = loader.loadClass("HelloWorld", true);

    Object obj = objClass.newInstance();

    Method[] methods=objClass.getMethods();
    for(Method m :methods)
    {
    System.out.println(m.getName());
    MethodDescriptor md = new MethodDescriptor(m);
    System.out.println(md.getParameterDescriptors());
    //這里得到的參數(shù)描述都是null
    //ParameterDescriptor[] PDS = md.getParameterDescriptors();
    System.out.println("************************");
    ///
    }
    }
    }

      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-03-30 19:26 | 王躍峰
    上面代碼用的反射肯定是不行了,目前就是看對(duì)應(yīng)class的反編譯結(jié)果,主要是不知道方法怎么和常量池里的參數(shù)名字聯(lián)系起來(lái).  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-03 00:22 | 獨(dú)孤求敗
    我寫了個(gè)程序驗(yàn)證了下,是不能獲得方法的參數(shù)名字的...
    后來(lái),我查看了常量池,里面好像沒有參數(shù)的名字啊,只有方法的參數(shù)類型描述,沒有名字。不知道你是用什么工具看到的?我是看不到:)
    我猜想,保存方法參數(shù)的名字也沒有意義啊,只需保存參數(shù)的類型就可以了,因?yàn)檎{(diào)用一個(gè)方法時(shí),只需驗(yàn)證其參數(shù)類型是否滿足即可!
    不值得,閣下想從class文中獲得方法的參數(shù)名字做什么用?@王躍峰
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-03 18:16 | 王躍峰
    謝謝老兄關(guān)注這個(gè)問題.
    我想先反編譯出原函數(shù)的樣子,然后考慮反編譯源代碼.
    常量池只要全部枚舉,有參數(shù)名字的,就是順序不一定是參數(shù)的順序了,我考慮了很久,目前主要思路還是停留在考慮method_info的code屬性的code里,我企圖從這著字節(jié)碼得到一些信息.
    我看過jad反編譯的代碼,只有這個(gè)工具可以看到反編譯函數(shù)的參數(shù)名字,我想在classfile里應(yīng)該有個(gè)對(duì)應(yīng)關(guān)系.

    @獨(dú)孤求敗
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-03 18:22 | 王躍峰
    關(guān)于枚舉常量池,我這里有網(wǎng)上一位大哥開發(fā)的一個(gè)源碼可以做到這一點(diǎn).我也是企圖在這個(gè)源代碼上進(jìn)一步研究.
    @獨(dú)孤求敗
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-03 22:27 | 獨(dú)孤求敗
    我用JAD試了一下,的確是你說(shuō)的那樣的,但是方法的形參名字是存在了該方法的局部變量表里了:)

    現(xiàn)在,我知道怎么做了,方法的形參被看作局部變量了,所以你需要去查看該方法的局部變量表,如果該方法有形參的話,則局部變量表中的第一個(gè)元素就是第一個(gè)形參,第二個(gè)元素就是第二個(gè)形參,其他類推

    你從局部變量表中可以查到這個(gè)局部變量的名字是存在哪個(gè)常量池表項(xiàng)里的,直接從常量池中獲取即可!你沒有必要枚舉常量池的:)
    祝順利:)
    @王躍峰
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-07 13:58 | 王躍峰
    非常感謝!感覺這回應(yīng)該很接近目的了.

    class文件結(jié)構(gòu)中的method_info里面沒有局部變量表呀?

    怎樣得到它?@獨(dú)孤求敗
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-07 14:11 | 王躍峰
    每個(gè)method_info表都包含了與方法相關(guān)的一些信息,包括方法名和描述符(即方法的返回值及參數(shù)類型)。如果一個(gè)方法既非abstract也非native,那么method_info表則包含方法局部變量所需的??臻g長(zhǎng)度、為方法所捕獲的異常表、字節(jié)碼序列以及可選的行號(hào)表和局部變量表等信息
    _______________________________________________________

    上面有些東西難理解,比如method_info表包含的??臻g長(zhǎng)度、為方法所捕獲的異常表、字節(jié)碼序列以及可選的行號(hào)表和局部變量表等信息.

    我們知道m(xù)ethod_info的結(jié)構(gòu)是這樣的:
    method_info{
    u2 acces_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attribute_count]
    };

    "方法所捕獲的異常表" 是對(duì)應(yīng)異常屬性
    "字節(jié)碼序列" 是對(duì)應(yīng)code屬性

    但是"??臻g長(zhǎng)度"\"可選的行號(hào)表"\"局部變量表"沒有對(duì)應(yīng)的結(jié)構(gòu)啊.

    在  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-08 12:43 |

    我已經(jīng)知道了,局部變量表在code屬性的屬性里,謝謝你啊,有空一定常來(lái)看你的博客,非常感謝!

    @獨(dú)孤求敗
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-04-09 22:23 | 獨(dú)孤求敗
    呵呵...不客氣:)
    @王
      回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-05-07 18:09 | FutureBoy
    請(qǐng)教一下: 兩個(gè)字符串常量"Hello"和"World"存放在什么地方? CLASS文件中哪里有對(duì)它們的表述?   回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2008-05-14 16:53 | 獨(dú)孤求敗
    @FutureBoy
    在常量池里啊:)
    關(guān)于常量池的表述,請(qǐng)參考上一篇博客“Class文件格式解析 ”  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例[未登錄] 2009-04-13 17:32 | 阿飛
    太厲害了!  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例[未登錄] 2010-12-21 15:17 | lynn
    寫得太好了!高手。有機(jī)會(huì)的話希望能請(qǐng)教你些問題。  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例[未登錄] 2012-05-10 16:21 | wei
    寫的真好  回復(fù)  更多評(píng)論
      
    # re: 一個(gè)解析Java Class文件的實(shí)例 2013-02-03 10:07 | dereky
    好強(qiáng)大啊  回復(fù)  更多評(píng)論
      

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 日韩中文字幕在线免费观看| 亚洲av最新在线网址| 亚洲国产日产无码精品| 黄色一级毛片免费看| 一色屋成人免费精品网站| 精品亚洲综合在线第一区| 国产AV无码专区亚洲AV琪琪| 亚洲人成在线免费观看| 亚洲成A∨人片在线观看不卡| 国产精品亚洲а∨天堂2021| 在线观看免费人成视频色9| 亚洲第一精品在线视频| 一级**爱片免费视频| 在线看片无码永久免费aⅴ| 亚洲毛片基地日韩毛片基地 | 美女隐私免费视频看| 免费AA片少妇人AA片直播| 亚洲AV无码久久精品蜜桃| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 一级特级女人18毛片免费视频| 成人激情免费视频| 97se亚洲综合在线| 在线成人精品国产区免费| 亚洲日韩国产一区二区三区| 亚洲第一成年免费网站| 亚洲免费网站观看视频| 亚洲精品欧洲精品| 久久精品乱子伦免费| 亚洲无码在线播放| fc2免费人成在线视频| 免费一看一级毛片人| 亚洲第一成年免费网站| 成人性生免费视频| 美女视频黄免费亚洲| 国产2021精品视频免费播放| 亚洲尹人香蕉网在线视颅| 久久久久久国产精品免费免费男同| 亚洲伊人色欲综合网| 国产一级黄片儿免费看| 夜夜春亚洲嫩草影院| 精品乱子伦一区二区三区高清免费播放|