jvm字節(jié)碼

jvm字節(jié)碼

jvm字節(jié)碼一般結(jié)構(gòu)

使用javap -verbose 命令分析一個類,得到的是一個類字節(jié)碼的魔數(shù),版本號,常量池,類信息,類的構(gòu)造方法,類的方法信息,類變量與成員變量等信息。當(dāng)然也可以配合jclasslib這個idea插件來學(xué)習(xí)字節(jié)碼內(nèi)容

  1. 魔數(shù):.class字節(jié)碼的前四個字節(jié)即為魔數(shù)。魔數(shù)為固定值:0xCAFEBABE
  2. 版本號:魔數(shù)之后的四個字節(jié)即為版本號,前兩個字節(jié)為 minor version 次版本號,后兩個字節(jié)為 major version 主版本號。
  3. 常量池:主版本號之后的即為常量池入口,常量池是可變的,不確定的??梢詫⒊A砍乜醋鲱愇募馁Y源倉庫。常量池中主要存儲兩類常量:字面常量和符號引用。字面常量如文本字符串。符號引用:類和接口的全局限定名,字段的名稱和描述符,方法的名稱和描述符。
    常量池由常量池數(shù)量和常量池表構(gòu)成,
    • 常量池數(shù)量
      常量池數(shù)量在主版本號之后,占據(jù)兩個字節(jié)。
    • 常量池表
      在常量池數(shù)量之后。常量池數(shù)組中不同元素的類型結(jié)構(gòu)是不同的,因此長度不同。但是每一種元素的第一個數(shù)據(jù)都是一個u1類型,該位置是標志位,占用一個字節(jié),jvm在解析常量池時,會根據(jù)u1類型來獲取元素的具體類型。
  4. 類信息:
    • 類的訪問修飾符(u2)
    • 類的名字(常量池索引)
    • 父類名字(常量池索引)
    • 接口個數(shù)
    • 接口表
  5. 成員變量:
    • 成員變量數(shù)(u2)
    • 成員變量表(包含類變量和和實例變量)
      • 權(quán)限描述符
      • 名字索引
      • 類型描述符索引
      • 屬性數(shù)
      • 屬性信息
  6. 方法
    • 方法個數(shù)(u2)
    • 方法表
      • 權(quán)限描述符(u2)
      • 名字索引(u2)
      • 類型描述符索引(u2)
      • 屬性數(shù)(u2)
      • 屬性信息
        屬性信息結(jié)構(gòu)
        • 屬性類型索引(u2)
        • 屬性信息長度(u4),本方法屬性之后的信息長度
        • 最大棧深(u2)
        • 最大局部變量數(shù)(u2),包含方法的參數(shù)和this。
        • 操作碼表長度(u4)
        • 操作碼表(u2,java虛擬機指令集)
        • 異常表長度
        • 異常表
          • 作用的開始行
          • 作用的結(jié)束行
          • 異常出現(xiàn)跳轉(zhuǎn)行
          • 異常名字索引
        • 屬性表長度(u2)
        • 屬性表:屬性表是code的屬性,如linenumbertype,存儲源代碼和字節(jié)碼行號對應(yīng)關(guān)系。localvariabletable,存儲方法中變量的類型,名字和描述。
          • 屬性名索引(u2)
          • 屬性長度(u4)
          • 屬性數(shù)(u2):表示有幾個屬性
            linenumbertype屬性內(nèi)容結(jié)構(gòu):
            {
            對應(yīng)關(guān)系:u4類型,有多個,(u2-u2)表示字節(jié)碼中行號和源代碼行號的對應(yīng)關(guān)系
            }
            localvariabletable屬性內(nèi)容結(jié)構(gòu):
            {
            局部變量在字節(jié)碼中開始作用的位置:u2類型
            局部變量在字節(jié)碼中作用的長度:u2類型
            局部變量名稱索引:u2類型
            局部變量描述符索引:u2類型
            局部變量localvariabletable中的索引
            }
  7. 屬性
    • 屬性數(shù)量(u2)
    • 屬性表
      • 屬性名索引(u2)
      • 屬性值
        屬性值長度(u4)
        屬性值索引(u2)

jvm class文件是線性的,但可以嘗試使用json理解字節(jié)碼的結(jié)構(gòu),以下為json結(jié)構(gòu)。
開頭表示線性排序表示*
屬性帶" x"表示可以有多個

{
    "*Magic Number":"0xCAFEBABE",
    "Version":{
        "*minor version":"u2",
        "*major version":"u2"
    },
    "Constant Pool":{
        "*Constant_count":"u2",
        "*Constant_info":[
            "類型眾多,參照表格,基本結(jié)構(gòu)為u1+n個字節(jié)。"            
        ]
    },
    "Class info":{
        "*Access Flags":"u2,類的訪問修飾符",
        "*This Class Name":"u2類名",
        "*Super Class Name":"u2 父類名字",
        "*Interfaces_count":"接口數(shù)",
        "*Interfaces_name":["接口名有多個"]
    },
    "Fields":{
        "*Fields_count":"u2,成員變量數(shù)",
        "Fields_info *x":[
            {
               "*access_flags":"u2,權(quán)限修飾符",
               "*name_index":"u2,變量名稱索引" ,
               "*descriptor_index":"u2,類型描述符索引",
               "*attribute_count":"u2,屬性數(shù)目",
               "attribute_info *x":[
                   {}
               ]
            }
        ]

    },
    "Methods":{
        "*methods_count":"u2,方法數(shù)",
        "methods_info *x":[
            {
                "*access_flags":"u2,訪問修飾符",
                "*name_index":"u2,方法名稱索引",
                "*descriptor_index":"u2,類型描述符索引",
                "*attribute_count":"u2,屬性數(shù)目",
                "attribute_info *x":[
                    {
                        "*attribute_name_index":"u2,屬性名索引",
                        "*attribute_length":"u4,屬性長度,指之后數(shù)據(jù)長度",
                        "*max_stack":"u2,最大棧深",
                        "*max_locals":"u2,最大局部變量數(shù)",
                        "*code_length":"u4,操作碼長度",
                        "code":[
                            "*code1",
                            "*code2",
                            "*...code為u1組成"
                        ],
                        "*exception_table_length":"u2,異常表長度",
                        "exception_table":[
                            {
                            "*Start_Pc":"u4,異常開始作用行",
                            "*Eed_Pc":"u4,異常結(jié)束作用行",
                            "*Handler_Pc":"u4,異常發(fā)生跳轉(zhuǎn)行",
                            "*Exception_name_index":"u2,異常名索引"
                            }

                        ],
                        "*attribute_count":"u2,屬性數(shù)目",
                        "attribute_info *x":[
                            {
                                "*attribute_name_index":"u2,屬性名索引",
                                "*attribute_length":"u4,屬性長度,指之后數(shù)據(jù)長度",
                                "*attribute_count":"表示有幾個對應(yīng)關(guān)系",
                                "attribute_info":[
                                    {
                                        "*linenumbertype":"字節(jié)碼和源碼行號對照表,u4類型,有多個,(u2-u2)表示字節(jié)碼中行號和源代碼行號的對應(yīng)關(guān)系"
                                    },
                                    {
                                        "localvariabletable":"局部變量表",
                                        "*局部變量在字節(jié)碼中開始作用的位置":"u2類型",
                                        "*局部變量在字節(jié)碼中作用的長度":"u2類型",
                                        "*局部變量名稱索引":"u2類型",
                                        "*局部變量描述符索引":"u2類型",
                                        "*局部變量localvariabletable中的索引":"u2"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    },
    "Attributes":{
        "*attribute_count":"u2,屬性數(shù)目",
        "attribute_table":[
            {
            "*attribute_name_index":"u2,屬性名索引",
            "*attribute_length":"屬性值長度",
            "*attribute_index":"屬性值索引"
            }

        ]
    }    
}

synchronized 關(guān)鍵字

  • synchronized方法 在方法上加標識符
  • synchronized靜態(tài)方法 在方法上加標識符
  • synchronized代碼塊 編譯出不同兩個指令集,在中間的表示為同步的代碼塊。

靜態(tài)變量和一般變量

  • 靜態(tài)變量和靜態(tài)代碼塊的指令集都在clinit()方法中執(zhí)行
  • 實例變量的值和代碼塊都在init() 方法中執(zhí)行

字節(jié)碼中的異常

  • jdk統(tǒng)一采用異常表的方式處理異常
  • 當(dāng)異常處理存在finally語句塊時,現(xiàn)代化的jvm采取的方式是將finally語句塊的字節(jié)碼拼接到每一個catch語句塊后面,有多超catch語句塊,就拼接多少次。
  • 方法后throws的異常會在method_info下生成一個異常數(shù)組

棧幀和操作數(shù)棧,直接引用和間接引用

  • 棧幀是一種幫助虛擬機執(zhí)行方法調(diào)用與方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)。
  • 棧幀本身是一種數(shù)據(jù)結(jié)構(gòu),封裝了方法的局部變量表,動態(tài)鏈接信息,方法的返回值和操作數(shù)棧等信息。
  • 有些符號引用在類加載階段或第一次使用時就轉(zhuǎn)換為直接引用,另一些符號引用則是在每次運行期轉(zhuǎn)為直接引用。這種轉(zhuǎn)換叫做動態(tài)鏈接,在java中體現(xiàn)為多態(tài)性。
  • 棧幀結(jié)構(gòu)
    {
    局部變量表
    操作數(shù)棧
    動態(tài)鏈接
    返回地址
    幀數(shù)據(jù)區(qū)
    }

重寫和重載的不同

  • 分派:確定執(zhí)行哪個方法的過程

  • 靜態(tài)類型:引用類型,不會被改變、在編譯器可知。

  • 動態(tài)類型:實例對象類型,會變化、在運行期才可知

  • 靜態(tài)分派:調(diào)用重載的方法。調(diào)用同一個類的不同的方法,編譯后會指向不同的符號引用(方法的參數(shù)不同,確定不同方法),這是顯而易見的,從代碼就可以看出方法調(diào)用的不同。

  • 動態(tài)分派:調(diào)用重寫的方法,若有兩個父類引用指向不同的兩個子類對象,編譯后指向相同的符號引用,具體執(zhí)行哪個實例方法是由jvm決定的。

  • invokevirtual:只要是調(diào)用public方法,編譯后都是invokevirtual指令操作,

    • invokevirtual指令執(zhí)行的第一步 = 確定接受者的實際類型
    • invokevirtual指令執(zhí)行的第二步 = 將常量池中類方法符號引用解析到不同的直接引用上
  • 調(diào)用重載的方法也是invokevirtual指令,這是因為重載的方法也有可能會被重寫,所有的public方法都采用invokevirtual指令,也是為了更好的體現(xiàn)多態(tài),多態(tài)在jvm層面是由invokevirtual指令實現(xiàn)的

  • 動態(tài)綁定具體的調(diào)用過程為:

    1. 首先會找到被調(diào)用方法所屬類的全限定名
    2. 在此類的方法表中尋找被調(diào)用方法,如果找到,會將方法表中此方法的索引項記錄到常量池中(這個過程叫常量池解析),如果沒有,編譯失敗。
    3. 根據(jù)具體實例化的對象找到方法區(qū)中此對象的方法表,再找到方法表中的被調(diào)用方法,最后通過直接地址找到字節(jié)碼所在的內(nèi)存空間。

棧指令集和寄存器指令集

  • jvm是基于棧的指令集,基于棧的指令集主要的操作有入棧和出棧兩種
  • 基于棧的指令集可以在不同平臺之間移植,基于寄存器的指令集是與硬件相關(guān)的。
  • 基于棧的指令集的缺點在于完成相同操作,指令的數(shù)量多運行速度慢?;诩拇嫫鞯闹噶罴俣葎t要快得多。

動態(tài)代理

  • 在java基礎(chǔ)中了解到了動態(tài)代理這種模式,在jvm層面,動態(tài)代理的意思是代理類的字節(jié)碼是在運行期由jvm生成。
  • 生成的代理類里面,最終還是會調(diào)用自己寫的InvocationHandler實現(xiàn)類里面的invoke方法,
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容