《深入理解Java虚拟机》读书笔记五
生活随笔
收集整理的這篇文章主要介紹了
《深入理解Java虚拟机》读书笔记五
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
第六章 類文件結(jié)構(gòu)
1、無關(guān)性的基石
- 各種不同平臺的虛擬機(jī)與所有平臺都統(tǒng)一使用程序存儲格式——字節(jié)碼是構(gòu)成平臺無關(guān)的基石。
- 實(shí)現(xiàn)語言無關(guān)性的基礎(chǔ)仍然是虛擬機(jī)和字節(jié)碼存儲格式,Java虛擬機(jī)不和包括Java在內(nèi)的任何語言綁定只能與class文件這種特定的二進(jìn)制文件格式所關(guān)聯(lián)。
- class文件包含了Java虛擬機(jī)指令集和符號表以及若干其他輔助信息。
2、class文件結(jié)構(gòu)
概述:
- class文件是一組以八位字節(jié)為基礎(chǔ)的二進(jìn)制流,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊的排列在class文件中。
- class文件格式采用一種類似于C語言結(jié)構(gòu)的偽數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù),這種偽數(shù)據(jù)結(jié)構(gòu)包含兩種數(shù)據(jù)類型無符號和表。
- 無符號數(shù)屬于基本數(shù)據(jù)類型,從u1、u2、u4、u8來分別表示一個(gè)字節(jié)、兩個(gè)字節(jié)、四個(gè)字節(jié)、八個(gè)字節(jié)的無符號數(shù)。無符號數(shù)可以用來描述數(shù)字、索引、引用7數(shù)量值或者按照utf-8編碼構(gòu)成字符串值。
- 表是由多個(gè)無符號或者其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型。所有的表都習(xí)慣性地以“_info”結(jié)尾。表示描述有層次關(guān)系的復(fù)合結(jié)構(gòu)的數(shù)據(jù),整個(gè)class文件本質(zhì)就是一張表。
魔數(shù):
- 每個(gè)class文件的頭四位為魔數(shù),作用是確定這個(gè)文件是否是一個(gè)能被虛擬機(jī)引用接受的class文件,class文件的魔數(shù)值是0xCAFEBABE。
class文件的版本:
- 緊接著魔數(shù)的四個(gè)字節(jié)存儲的是class文件的版本號,第5和第6個(gè)字節(jié)是次版本號,第7和第8個(gè)是主版本號。
常量池:
- 緊接著主版本號的是常量池入口,常量池可以理解為class文件中的資源倉庫,它是class文件結(jié)構(gòu)中與其他項(xiàng)目關(guān)聯(lián)的最多的數(shù)據(jù)類型,也是占有class文件空間最大的數(shù)據(jù)項(xiàng)目之一,同時(shí)它是class文件第一個(gè)出現(xiàn)的表類型數(shù)據(jù)。
- 由于常量池的常量數(shù)值不固定,所以在常量池的入口需要放置一項(xiàng)u2類型的數(shù)據(jù)代表常量池容量計(jì)數(shù)值,計(jì)數(shù)是從一開始的。
- 常量池主要存放兩大常量,字面量和符號引用,字面量比較接近于Java語言層面的常量概念,如文字字符串,聲明為final的常量池等。
- 符號引用包括類和接口的全限定名,字段的名稱和描述符,方法的名稱和描述符。
- 常量池中每一項(xiàng)常量都是一個(gè)表,根據(jù)標(biāo)志位來確定項(xiàng)目類型。 package com.ecut.clazz;public class TestClass {private int m;public int inc() {return m + 1;}
}
字節(jié)碼文件對應(yīng)的16進(jìn)制如下圖:
常量容量計(jì)數(shù)值為22則代表有21個(gè)常量,第一個(gè)常量的標(biāo)志位是0x0A,根據(jù)下表可知對應(yīng)的項(xiàng)目類型是CONSTANT_Methodref_info,此類型代表類中方法的符號引用,這個(gè)項(xiàng)目類型對應(yīng)的結(jié)構(gòu)有兩個(gè)index,第一個(gè)index為0x0004為即十進(jìn)制4,第二個(gè)index為0x0012即十進(jìn)制18。第二個(gè)常量是以此類推,這個(gè)項(xiàng)目類型為CONSTANT_Fieldref_info,第一個(gè)index為0x0003即十進(jìn)制3,第二個(gè)index為0x0013即十進(jìn)制19。 - 剩下的常量可以借助class文件字節(jié)碼的工具javap來輸出TestClass.class文件字節(jié)碼內(nèi)容。采用javap -verbose TestClass.class命令。
訪問標(biāo)志:
- 在常量池結(jié)束之后,緊接著的兩個(gè)字節(jié)代表訪問標(biāo)志,這個(gè)標(biāo)志用于標(biāo)識一些類或者接口層次的訪問信息,包括是類還是接口,是否為public類型,是否為abstract。具體的標(biāo)志位以及標(biāo)志的含義如下表:
- access_flag中一共有16個(gè)標(biāo)志位可以使用,當(dāng)前只定義了其中8個(gè),沒有使用到的標(biāo)志位要求一律為0。
- TestClass被public修飾并且使用了jdk1.2之后的編譯器進(jìn)行了編譯,因此它的access_flag標(biāo)志值應(yīng)該為100001=0x21。
類索引、父類索引、接口索引:
- 類索引用于確定表達(dá)這個(gè)類的全限定名,父類索引用于確定這個(gè)類的父類的全限定名,接口索引集合就用于描述這個(gè)類實(shí)現(xiàn)了那些接口,這些接口按implements語句從左到右排列在接口縮影集合中。
字段表集合:
- 用于描述接口或類中聲明的變量。字段包括類級變量以及實(shí)例級變量,但不包括在方法內(nèi)部聲明的局部變量。
- 字段集合不會列出從超類中或者父類接口中繼承而來的字段,但有可能列出原本沒有的字段,如在內(nèi)部類中為了保持外部類的訪問性會加入外部類的實(shí)例字段。
- 字段包含信息:字段的作用域(public、private、protected),是實(shí)例變量還是類變量(static修飾符),可變性(final)、并發(fā)性(volatile修飾符,是否強(qiáng)制從主內(nèi)存讀寫)、可否被序列化(transient修飾符),字段數(shù)據(jù)類型(基本類型、對象、數(shù)組),字段名稱。
- 字段修飾符放在access_flag項(xiàng)目中,它包含兩項(xiàng)name_index和descript_index,他們都是對常量池的引用,分別代表字段的簡單名稱和字段的描述符。
- 字段表結(jié)構(gòu)如下圖所示:
- 結(jié)合之前的常量池中的常量可知類索引為?com/ecut/clazz/TestClass,父類索引為?java/lang/Object。TestClass包含一個(gè)被private修飾的int 類型的m。?????????????????????????????????????????????????
方法表集合:
- 方法表集合包括訪問標(biāo)志、名稱索引、描述符索引、屬性集合。如果父類方法在子類方法中沒有被重寫,方法表集合中不會有來自父類的方法信息。
- 方法表結(jié)構(gòu)如下圖:
屬性表集合:
- 在class文件中,字段表方法表都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。
- 屬性集合的結(jié)構(gòu)如下圖:
- 結(jié)合常量池對文件進(jìn)行分析可得,方法容量為2包含了兩個(gè)方法,第一個(gè)方法時(shí)public修飾void init,該方法包含了一個(gè)屬性code,code操作數(shù)棧最大值為1.第二個(gè)方法時(shí)public修飾的int inc()該方法也包含了一個(gè)屬性code。
- Exception屬性列舉出方法中可能拋出的異常,也就是方法描述時(shí)在throws關(guān)鍵字后面列出的異常。
- LineNumberTable屬性用于描述Java源碼行號和字節(jié)碼行號之間的對應(yīng)關(guān)系。
- LocalVariableTable屬性用來描述棧幀中局部變量與Java源碼中定義的變量之間的關(guān)系。
- SourceFile屬性用來記錄生成這個(gè)class源碼文件名稱。
3、字節(jié)碼指令
概述:
- Java虛擬機(jī)的指令由一個(gè)字節(jié)長度的,代表著某中特定操作含義的數(shù)字(稱為操作碼)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)(稱為操作數(shù))而構(gòu)成。
- Java虛擬機(jī)采用面向操作數(shù)棧的架構(gòu)所以大多數(shù)指令都不包含操作數(shù)只有一個(gè)操作碼。
- 在Java虛擬機(jī)的指令集中大多數(shù)指令都包含了其操作鎖對應(yīng)的數(shù)據(jù)類型信息。
加載和存儲指令:
- 加載存儲指令用來將數(shù)據(jù)在棧幀中的局部變量和操作數(shù)棧之間來回傳輸。
- 將一個(gè)局部變量加載到操作數(shù)棧iload。
- 將一個(gè)數(shù)值從操作數(shù)棧中存儲到局部變量表istore。
- 將一個(gè)常量加載到操作數(shù)棧bipush。
- 擴(kuò)充局部變量表的訪問索引的指令wide。
運(yùn)算或算法指令:
- 運(yùn)算或算法指令用于對兩個(gè)操作數(shù)棧上的值進(jìn)行某中特定的運(yùn)算,并把結(jié)果重新存入操作數(shù)棧的棧頂。
類型轉(zhuǎn)換指令:
?
- 類型轉(zhuǎn)換指令可以將兩種不同的數(shù)值類型進(jìn)行相互轉(zhuǎn)換,這種轉(zhuǎn)換操作一般用于實(shí)現(xiàn)用戶代碼中的顯示類型轉(zhuǎn)換操作。
- int——》long——》float——》double。
對象創(chuàng)建和訪問指令:
- 雖然類實(shí)例和數(shù)組都是對象但是使用不同的指令來完成創(chuàng)建。
- 創(chuàng)建類實(shí)例的指令 new。
- 創(chuàng)建數(shù)組的指令 newarray。
- 訪問類字段和實(shí)例字段 putfield 、getfield、getstatic。
操作數(shù)棧管理指令:
- 將操作數(shù)棧的棧頂一個(gè)元素出棧pop。
- 將棧頂兩個(gè)數(shù)值交換swap。
控制轉(zhuǎn)移指令:
- 在有條件或者無條件的修改PC寄存器的值。
- 條件分支ifea、iflt。
- 復(fù)合條件分支lookupswich。
- 無條件goto 。
方法調(diào)用和返回指令:
- 方法調(diào)用指令和返回值無關(guān),而方法返回指令是根據(jù)返回值類型來區(qū)分的。
- invokevirtual用于調(diào)用對象的實(shí)例方法。
- invokestatic用于調(diào)用類方法。
異常處理指令:
- 在Java程序中顯示拋出異常的操作都有athrow指令來完成。
- Java虛擬規(guī)范還規(guī)定許多運(yùn)行時(shí)異常會在其他指令檢測到異常時(shí)自動拋出。
同步指令:
- Java虛擬機(jī)可以支持方法級的同步和方法內(nèi)部一段指令序列的同步,這兩種同步結(jié)構(gòu)都是使用管程(Monitor)來支持的。
推薦博客鏈接:
https://blog.csdn.net/u010349169/column/info/jvm-principle
轉(zhuǎn)載請于明顯處標(biāo)明出處:
https://www.cnblogs.com/AmyZheng/p/10537013.html
轉(zhuǎn)載于:https://www.cnblogs.com/AmyZheng/p/10537013.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的《深入理解Java虚拟机》读书笔记五的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 测试必知的150个Linux命令
- 下一篇: 如何在本地连接服务器上的MySQL