一步一步實(shí)現(xiàn)簡單安卓性能監(jiān)控SDK之熟悉java字節(jié)碼

什么是字節(jié)碼

說起字節(jié)碼就不得不說兩個(gè)東西:java語言規(guī)范和java虛擬機(jī)規(guī)范。
Java語言規(guī)范只是規(guī)定了Java語言相關(guān)的約束以及規(guī)則,而虛擬機(jī)規(guī)范則才是真正從跨平臺的角度去設(shè)計(jì)的。

java語言的跨平臺在一定程度上也是字節(jié)碼帶來的益處

java語言是跨平臺的,所謂一次編寫,到處運(yùn)行。之所以是跨平臺的,就是java定義了一套與操作系統(tǒng),硬件無關(guān)的字節(jié)碼格式,這個(gè)字節(jié)碼就是用java class文件來表示的,java class文件內(nèi)部定義了虛擬機(jī)可以識別的字節(jié)碼格式,這個(gè)格式是平臺無關(guān)性的,在linux系統(tǒng)或者在windows系統(tǒng)上都是一致的。這個(gè)就好比html文件,我們定義好規(guī)范,這個(gè)系統(tǒng)只要去按照規(guī)范顯示出來里面的內(nèi)容就好了。好比html就是class文件,瀏覽器就是虛擬機(jī)一樣,通過瀏覽器去執(zhí)行html的渲染過程,我們無論是用手機(jī),Windows系統(tǒng),蘋果系統(tǒng)上網(wǎng),顯示出來的內(nèi)容都是一樣。 java虛擬機(jī)可以從class文件中加載預(yù)定義的字節(jié)碼,也可以從網(wǎng)絡(luò),數(shù)據(jù)庫,消息文件中加載字節(jié)碼。

不僅僅java才有字節(jié)碼

例如groovy 、Scala等等編譯之后都可以轉(zhuǎn)換為字節(jié)碼,運(yùn)行在jvm虛擬機(jī)上。

class文件是最終歸宿.png

java代碼被編譯之后,最終輸出了.class文件。這個(gè)文件就是字節(jié)碼文件,會被jvm虛擬機(jī)執(zhí)行。

class 文件 結(jié)構(gòu)

圖片來源自網(wǎng)絡(luò)

兩種數(shù)據(jù)類型:無符號數(shù)和表。

無符號數(shù)就是u1、u2、u4、u8來分別代表1個(gè)、2個(gè)、4個(gè)、8個(gè)字節(jié)。表是由
多個(gè)無符號數(shù)或其他表構(gòu)成的復(fù)合數(shù)據(jù)類型,以“_info”結(jié)尾。在表開始位置,
通常會使用一個(gè)前置的容量計(jì)數(shù)器,因?yàn)楸硗ǔR枋鰯?shù)量不定的多個(gè)數(shù)據(jù)。

來看看,

class 數(shù)據(jù)結(jié)構(gòu) .png

其中上圖中的第1、2、 3 行為header ,共暫用8個(gè)字節(jié)。

header中包含的版本號信息,和jdk的兼容性有關(guān)。只有高版本編譯的class文件可以兼容低版本的jdk。也就是向下兼容。

實(shí)際分析

class文件結(jié)構(gòu)之header

還記得上一篇文章,我們寫的測試程序嗎,代碼如下
<pre>
public class Main {
public static void main(String args [] ) {
System.out.println(" program main method execute ! ");
}
}
</pre>

用UE打開

UE open

Header部分,前八位:00000000h: CA FE BA BE 00 00 00 31 ; 漱壕...1

前四位(U4)為CAFEBABE是魔數(shù),緊接著的00 00 ,次版本號(U2),再接著的 00 31 代表主版本(U2) 十進(jìn)制是 49 ,根據(jù)如下的jdk版本對照表,得知,最低兼容 1.5

jdk版本對照表.png

class文件結(jié)構(gòu)之常量池

常量池,是保存所有常量的一個(gè)區(qū)間,包括但不限于字符串,還包含方法名,字面量等等。當(dāng)前類的類名, 字段名, 方法名, 各個(gè)字段和方法的描述符, 對當(dāng)前類的字段和方法的引用信息, 當(dāng)前類中對其他類的引用信息等等。可以說,它保存了當(dāng)前類的所有元數(shù)據(jù)?。。?/p>

常量池中包含的數(shù)據(jù)類型如下

常量池中的數(shù)據(jù)類型.png

下一步,00 22 ,代表常量池中常量的個(gè)數(shù),22 十進(jìn)制為34 ,表示常量池里有下標(biāo)為1~33的表項(xiàng)目。
下標(biāo)從1開始而不是0,是因?yàn)榈?個(gè)表項(xiàng)表示“不引用常量池中的任意一項(xiàng)”。具體表項(xiàng)看看下圖。

這些表項(xiàng)有一個(gè)共同的特點(diǎn)是,他們的TAG都是U1類型的?。。?/p>

常量池中的數(shù)據(jù)類型 2.png

幸虧他們的TAG都是U1類型的,否則,jvm該如何計(jì)算和分析呢!
分析第一項(xiàng):0A 為tag,十進(jìn)制為10對應(yīng)上方表格中的CONSTANT_Methodref_info , 這個(gè)methodref結(jié)構(gòu)是 u1 / u2 / u2 ,u1代表tag剛才說了,再看第二個(gè)u2代表的index為00 06 十進(jìn)制是6 ,指向方法聲明類描述符class_info中的索引。第三個(gè)元素u2 代表的index是 00 14 十進(jìn)制是 20,同樣指向字段描述。nameAndType_info的索引。綜合起來,這個(gè)0A 0006 0014 就代表了方法的描述。

其他部分的理解和分析,也都大致如此。

常量池主要為后面的_info字段服務(wù)的。

我們看看,剛才我們的那個(gè)類都有哪些常量。

逐個(gè)分析是比較麻煩的,可以使用JDK自帶的用于分析Class文件字節(jié)碼的工具javap

常量.png

class文件結(jié)構(gòu)之訪問標(biāo)志(access_flags)

訪問標(biāo)志.png

class文件結(jié)構(gòu)之 類索引、父類索引與接口索引集合(this_class 、 super_class 、 interfaces_count 和 interfaces)

  • this_class:**類索引,用于確定這個(gè)類的全限定名,占2字節(jié)
  • super_class:**父類索引,用于確定這個(gè)類父類的全限定名(Java語言不允許多重繼承,故父類索引只有一個(gè)。除了java.lang.Object類之外所有類都有父類,故除了java.lang.Object類之外,所有類該字段值都不為0),占2字節(jié)
  • interfaces_count:**接口索引計(jì)數(shù)器,占2字節(jié)。如果該類沒有實(shí)現(xiàn)任何接口,則該計(jì)數(shù)器值為0,并且后面的接口的索引集合將不占用任何字節(jié),
  • interfaces:**接口索引集合,一組u2類型數(shù)據(jù)的集合。用來描述這個(gè)類實(shí)現(xiàn)了哪些接口,這些被實(shí)現(xiàn)的接口將按implements語句(如果該類本身為接口,則為extends語句)后的接口順序從左至右排列在接口的索引集合中
    this_class、super_class與interfaces中保存的索引值均指向常量池中一個(gè)CONSTANT_Class_info類型的常量,通過這個(gè)常量中保存的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串

字段表集合(fields_count 和 fields)

fields_count:字段表計(jì)數(shù)器,即字段表集合中的字段表數(shù)據(jù)個(gè)數(shù)。占2字節(jié)

fields:字段表集合,一組字段表類型數(shù)據(jù)的集合。字段表用于描述接口或類中聲明的變量,包括類級別(static)和實(shí)例級別變量,不包括在方法內(nèi)部聲明的變量

在Java中一般通過如下幾項(xiàng)描述一個(gè)字段:字段作用域(public、protected、private修飾符)、是類級別變量還是實(shí)例級別變量(static修飾符)、可變性(final修飾符)、并發(fā)可見性(volatile修飾符)、可序列化與否(transient修飾符)、字段數(shù)據(jù)類型(基本類型、對象、數(shù)組)以及字段名稱。在字段表中,變量修飾符使用標(biāo)志位表示,字段數(shù)據(jù)類型和字段名稱則引用常量池中常量表示,字段表格式如下表所示:

Paste_Image.png

字段修飾符放在access_flags中,占2字節(jié)


字段修飾符.png

方法表集合(methods_count 和 methods)

methods_count:方法表計(jì)數(shù)器,即方法表集合中的方法表數(shù)據(jù)個(gè)數(shù)。

methods:方法表集合,一組方法表類型數(shù)據(jù)的集合。方法表結(jié)構(gòu)和字段表結(jié)構(gòu)一樣:

方法表結(jié)構(gòu).png

數(shù)據(jù)項(xiàng)的含義非常相似,僅在訪問標(biāo)志位和屬性表集合中的可選項(xiàng)上有略微不同

由于ACC_VOLATILE標(biāo)志和ACC_TRANSIENT標(biāo)志不能修飾方法,所以access_flags中不包含這兩項(xiàng),同時(shí)增加ACC_SYNCHRONIZED標(biāo)志、ACC_NATIVE標(biāo)志、ACC_STRICTFP標(biāo)志和ACC_ABSTRACT標(biāo)志

數(shù)據(jù)項(xiàng).png

屬性表集合(attributes_count 和 attributes)

在Class文件、屬性表、方法表中都可以包含自己的屬性表集合,用于描述某些場景的專有信息

與Class文件中其它數(shù)據(jù)項(xiàng)對長度、順序、格式的嚴(yán)格要求不同,屬性表集合不要求其中包含的屬性表具有嚴(yán)格的順序,并且只要屬性的名稱不與已有的屬性名稱重復(fù),任何人實(shí)現(xiàn)的編譯器可以向?qū)傩员碇袑懭胱约憾x的屬性信息。虛擬機(jī)在運(yùn)行時(shí)會忽略不能識別的屬性,為了能正確解析Class文件,虛擬機(jī)規(guī)范中預(yù)定義了虛擬機(jī)實(shí)現(xiàn)必須能夠識別的9項(xiàng)屬性:

Paste_Image.png

每種屬性均有各自的表結(jié)構(gòu)。這9種表結(jié)構(gòu)有一個(gè)共同的特點(diǎn),即均由一個(gè)u2類型的屬性名稱開始,可以通過這個(gè)屬性名稱來判段屬性的類型。

詳情參考下面的鏈接第二條。

參考:
https://github.com/waylau/java-virtual-machine-specification
http://blog.csdn.net/a19881029/article/details/16117251

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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