11_JVM學(xué)習(xí)筆記_字節(jié)碼剖析

字節(jié)碼剖析

示例代碼:

package com.leofight.jvm.bytecode;

public class MyTest1 {

    private int a = 1;


    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

反編譯信息如下:

classes javap -verbose com.leofight.jvm.bytecode.MyTest1
Classfile /Users/lz/IdeaProjects/jvm_lecture/out/production/classes/com/leofight/jvm/bytecode/MyTest1.class
  Last modified Jul 29, 2018; size 497 bytes
  MD5 checksum 962ee95ed59fdba7d6d90375419bc0ff
  Compiled from "MyTest1.java"
public class com.leofight.jvm.bytecode.MyTest1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#21         // com/leofight/jvm/bytecode/MyTest1.a:I
   #3 = Class              #22            // com/leofight/jvm/bytecode/MyTest1
   #4 = Class              #23            // java/lang/Object
   #5 = Utf8               a
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/leofight/jvm/bytecode/MyTest1;
  #14 = Utf8               getA
  #15 = Utf8               ()I
  #16 = Utf8               setA
  #17 = Utf8               (I)V
  #18 = Utf8               SourceFile
  #19 = Utf8               MyTest1.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = NameAndType        #5:#6          // a:I
  #22 = Utf8               com/leofight/jvm/bytecode/MyTest1
  #23 = Utf8               java/lang/Object
{
  public com.leofight.jvm.bytecode.MyTest1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field a:I
         9: return
      LineNumberTable:
        line 3: 0
        line 5: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/leofight/jvm/bytecode/MyTest1;

  public int getA();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field a:I
         4: ireturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/leofight/jvm/bytecode/MyTest1;

  public void setA(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #2                  // Field a:I
         5: return
      LineNumberTable:
        line 13: 0
        line 14: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/leofight/jvm/bytecode/MyTest1;
            0       6     1     a   I
}
SourceFile: "MyTest1.java"

字節(jié)碼文件16進(jìn)制

CAFEBABE 00000034 00180A00 04001409 00030015 07001607 00170100 01610100 01490100 063C696E 69743E01 00032829 56010004 436F6465 01000F4C 696E654E 756D6265 72546162 6C650100 124C6F63 616C5661 72696162 6C655461 626C6501 00047468 69730100 234C636F 6D2F6C65 6F666967 68742F6A 766D2F62 79746563 6F64652F 4D795465 7374313B 01000467 65744101 00032829 49010004 73657441 01000428 49295601 000A536F 75726365 46696C65 01000C4D 79546573 74312E6A 6176610C 00070008 0C000500 06010021 636F6D2F 6C656F66 69676874 2F6A766D 2F627974 65636F64 652F4D79 54657374 31010010 6A617661 2F6C616E 672F4F62 6A656374 00210003 00040000 00010002 00050006 00000003 00010007 00080001 00090000 00380002 00010000 000A2AB7 00012A04 B50002B1 00000002 000A0000 000A0002 00000003 00040005 000B0000 000C0001 0000000A 000C000D 00000001 000E000F 00010009 0000002F 00010001 00000005 2AB40002 AC000000 02000A00 00000600 01000000 09000B00 00000C00 01000000 05000C00 0D000000 01001000 11000100 09000000 3E000200 02000000 062A1BB5 0002B100 00000200 0A000000 0A000200 00000D00 05000E00 0B000000 16000200 00000600 0C000D00 00000000 06000500 06000100 01001200 00000200 13

Java字節(jié)碼結(jié)構(gòu)

Java字節(jié)碼整體結(jié)構(gòu)

Java字節(jié)碼整體結(jié)構(gòu)

完整的Java字節(jié)碼結(jié)構(gòu)

Class字節(jié)碼中有兩種數(shù)據(jù)類型:

  • 字節(jié)數(shù)據(jù)直接量:這是基本的數(shù)據(jù)類型材部。共細(xì)分為u1、u2、u4悼吱、u8四種,分別代表連續(xù)的1個(gè)字節(jié)良狈、2個(gè)字節(jié)后添、4個(gè)字節(jié)、8個(gè)字節(jié)組成的整體數(shù)據(jù)薪丁。
  • 表(數(shù)組):表是由多個(gè)基本數(shù)據(jù)或其他表遇西,既按照既定順序組成的大的數(shù)據(jù)結(jié)合。表是有結(jié)構(gòu)的严嗜,它的結(jié)構(gòu)體現(xiàn)在:組成表的成分所在的位置和順序都是嚴(yán)格定義好的粱檀。

使用javap -verbose命令分析一個(gè)字節(jié)碼文件時(shí),將會分析該字節(jié)碼文件的魔數(shù)漫玄、版本號茄蚯、常量池、類信息睦优、類的構(gòu)造方法渗常、類中的方法信息、類變量與成員變量等信息汗盘。

魔數(shù):所有的.class字節(jié)碼文件的前4個(gè)字節(jié)都是魔數(shù)皱碘,魔數(shù)值為固定值:0xCAFEBABE。
版本號::魔數(shù)之后的4個(gè)字節(jié)為版本信息衡未,前兩個(gè)字節(jié)表示minor version(次版本號)尸执,后兩個(gè)字節(jié)表示major version(主版本號),這里的版本號為 00 00 00 34缓醋,換算成十進(jìn)制,表示次版本號為0如失,主版本號為52。所以該文件的版本號為:1.8.0送粱⊥使螅可以通過java -version來驗(yàn)證這一點(diǎn)。

?  classes java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

常量池(constant pool):緊接著主版本號之后 就是常量池的入口。一個(gè)Java類中定義的很多信息都是由常量池來維護(hù)和描述的脆丁,可以將常量池看作是Class文件的資源倉庫世舰,比如說Java類中定義的方法與變量信息,都是存儲在常量池中槽卫。常量池中主要存儲兩類常量:字面量與符號引用跟压。字面量如文本字符串,Java中聲明為final的常量值等歼培,而符號引用如類和接口的全局限定名震蒋,字段的名稱和描述符,方法的名稱和描述符等躲庄。

常量池的總體結(jié)構(gòu): Java類所對應(yīng)的常量池主要由常量池?cái)?shù)量與常量池?cái)?shù)組(常量表)這兩部分公共組成查剖。常量池?cái)?shù)量僅跟在主版本號后面,占據(jù)2個(gè)字節(jié)噪窘;常量池?cái)?shù)組僅跟在常量池?cái)?shù)量之后笋庄。常量池?cái)?shù)組與一般的數(shù)組不同的是,常量池?cái)?shù)組中不同的元素的類型倔监、結(jié)構(gòu)都是不同的直砂,長度當(dāng)然也就不同;但是浩习,每一種元素的第一個(gè)數(shù)據(jù)都是u1類型哆键,該字節(jié)是個(gè)標(biāo)志位,占據(jù)1個(gè)字節(jié)瘦锹。JVM在解析常量池時(shí),會根據(jù)這個(gè)u1類型來獲取元素的具體類型闪盔。值的注意的是弯院,常量池?cái)?shù)組中元素的個(gè)數(shù) = 常量池?cái)?shù) - 1 (其中0暫時(shí)不使用),目的是滿足某些常量池索引值的數(shù)據(jù)在特定情況下需要表達(dá)【不引用任何一個(gè)常量池】的含義泪掀;根本原因在于听绳,索引為0也是一個(gè)常量(保留常量),只不過它位于常量表中异赫,這個(gè)常量就對應(yīng)null值椅挣;所以,常量池的索引從1而非0開始塔拳。

Class文件結(jié)構(gòu)中常量池中11中數(shù)據(jù)類型的結(jié)構(gòu)總表

在上面的表中描述了11種數(shù)據(jù)類型的結(jié)構(gòu)鼠证,其實(shí)在jdk1.7之后又增加了3種(CONSTANT_MethodHandle_info,CONSTANT_MethodType_info以及CONSTANT_InvokeDynamic_info)。這樣一共是14種靠抑。

在JVM規(guī)范中量九,每個(gè)變量/字段都有描述信息,描述信息主要的作用是描述字段的數(shù)據(jù)類型、方法的參數(shù)類型(包括數(shù)量荠列、類型與順序)與返回值类浪。根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型和代表無返回值的void類型都是用一個(gè)大寫字符來表示肌似,對象類型則使用字符L加對象的全限定名稱來表示费就。為了壓縮字節(jié)碼文件的體積,對于基本數(shù)據(jù)類型川队,JVM都只使用一個(gè)大寫字母來表示力细,如下所示:B - byte, C - char呼寸, D - double艳汽,F(xiàn) - float, I - int 对雪,J - long河狐,S - short, Z - boolean瑟捣, V - void馋艺,L - 對象類型,如Ljava/lang/String;

對于數(shù)組類型來說迈套,每一個(gè)維度使用一個(gè)前置的[來表示捐祠,如int[]被記錄為[I,String[][]被記錄為[[Ljava/lang/String;

用描述符描述方法時(shí),按照先參數(shù)列表桑李,后返回值的順序來描述踱蛀。參數(shù)列表按照參數(shù)的嚴(yán)格順序放在一組()之內(nèi),如方法:String getRealnamebyIdAndNickname(int id贵白,String name)的描述符為:(I,Ljava/lang/String;)Ljava/lang/String;

Access_Flag 訪問標(biāo)志:訪問標(biāo)志信息包括該Class文件是類還是接口率拒,是否被定義成public,是否是abstract禁荒,如果是類猬膨,是否被聲明成final。通過上面的源代碼呛伴,我們知道該文件是類并且是public勃痴。


0x 00 21:是0×0020和0×0001的并集,表示ACC_PUBLIC與ACC_SUPER 热康。

類索引:用于確定類的全限定名沛申,0×00 03 表示引用第3個(gè)常量,同時(shí)第3個(gè)常量引用第22個(gè)常量姐军,查找得#22 = Utf8 com/leofight/jvm/bytecode/MyTest1

父類索引:0×00 04 同理:#4.#23(java/lang/Object)

接口索引:接口有2+n個(gè)字節(jié)污它,前兩個(gè)字節(jié)表示的是接口數(shù)量,后面跟著就是接口的表。我們這個(gè)類沒有任何接口衫贬,所以應(yīng)該是0000德澈。

字段表集合:字段表用于描述類和接口中聲明的變量。這里的字段包含了類級別變量以及實(shí)例變量固惯,但是不包括方法內(nèi)部聲明的局部變量梆造。



0×00 02 :訪問標(biāo)志為private
0×00 05 : 字段名稱索引為#5镇辉,對應(yīng)的是”a”
0x 00 06 :描述符索引為#6烂斋,對應(yīng)的是”I”
0x 00 00 :屬性表數(shù)量為0罕模,因此沒有屬性表蝶念。

方法表:


方法中的每個(gè)屬性都是一個(gè)attribute_info結(jié)構(gòu)


JVM預(yù)定義了部分attribute适袜,但是編譯器自己也可以實(shí)現(xiàn)自己的attribute寫入class文件里,共運(yùn)行時(shí)使用疫萤。不同的attribute通過attribute_name_index來區(qū)分。

Code結(jié)構(gòu):Code attribute的作用是保存該方法的結(jié)構(gòu)钓丰,如所對應(yīng)的字節(jié)碼

attribute_length表示attribute所包含的字節(jié)數(shù),不包含attribute_name_index和attribute_length字段。

max_stack表示這個(gè)方法運(yùn)行的任何時(shí)刻所能達(dá)到的操作數(shù)棧的最大深度存筏。

max_locals表示方法執(zhí)行期間創(chuàng)建的局部變量的數(shù)目方篮,包含用來表示傳入的參數(shù)局部變量励负。

code_length表示該方法所包含的字節(jié)碼的字節(jié)數(shù)以及具體的指令碼

具體字節(jié)碼即是該方法被調(diào)用時(shí),虛擬機(jī)所執(zhí)行的字節(jié)碼

exception_table,這里存放的是處理異常的信息

每個(gè)exception_table表項(xiàng)由start_pc巾表,end_pc,handler_pc,catch_type組成。

start_pc和end_pc表示在code數(shù)組中的從start_pc到end_pc處(包含start_pc集币,不包含end_pc)的指令拋出的異常會由這個(gè)表項(xiàng)來處理

handler_pc表示處理異常的代碼的開始處鞠苟。catch_type表示會被處理的異常類型考榨,它指向常量池里的一個(gè)異常類冀惭。當(dāng)catch_type為0時(shí)震叙,表示處理所有的異常。

附加屬性
LineNumberTable:這個(gè)屬性用來表示code數(shù)組中的字節(jié)碼和Java代碼行數(shù)之間的關(guān)系散休。這個(gè)屬性可以用來在調(diào)試的時(shí)候定位代碼執(zhí)行的行數(shù)溃槐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昏滴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拂共,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姻几,死亡現(xiàn)場離奇詭異蛇捌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)俭驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門混萝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸嘀,“玉大人允粤,你說我怎么就攤上這事类垫±樱” “怎么了购撼?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵迂求,是天一觀的道長。 經(jīng)常有香客問我毫玖,道長凌盯,這世上最難降的妖魔是什么驰怎? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任县忌,我火速辦了婚禮,結(jié)果婚禮上装获,老公的妹妹穿的比我還像新娘穴豫。我一直安慰自己肋杖,他們只是感情好挖函,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布怨喘。 她就那樣靜靜地躺著梳庆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天存哲,我揣著相機(jī)與錄音贺辰,去河邊找鬼足淆。 笑死靠欢,一個(gè)胖子當(dāng)著我的面吹牛秧廉,可吹牛的內(nèi)容都是我干的拧粪。 我是一名探鬼主播拾因,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砾跃,長吁一口氣:“原來是場噩夢啊……” “哼壁熄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岖赋,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎亿柑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體花嘶,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莺禁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年翰灾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侈沪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亚脆,死狀恐怖柑营,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤鼻百,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布囤捻,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏砾隅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一攀痊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸狼电。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至物舒,卻和暖如春置蜀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灼卢。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工鞋真, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涩咖,地道東北人繁莹。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓闸昨,卻偏偏與公主長得像雪标,于是被迫代替她去往敵國和親村刨。 傳聞我的和親對象是個(gè)殘疾皇子嵌牺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容