日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

java dalvik_深入理解Android之Java虚拟机Dalvik

發布時間:2024/10/14 Android 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java dalvik_深入理解Android之Java虚拟机Dalvik 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

唯一有點特別之處的是常量池。什么東西會放在常量池呢?最容易想到的就是字符串了。對頭,這個Java源碼中的類名,方法名,變量名,居然都是以字符串形式存儲在常量池中。所以,圖2中的this_class和super_class分別指向兩個字符串,代表本類的名字和基類的名字。這兩個字符串存儲在常量池中,所以this_class和super_class的類型都是u2(索引,代表長度為2個字節)。

Class文件用javap工具可以很好得解析成圖2那樣的格式,我這里替大家解析了一把,結果如圖3所示(先顯示部分內容):

注意,解析方法為:javap -verbose xxxx.class

先來看看常量池。

2.1.1 常量池介紹

常量池看起來陌生,其實簡單得要死。注意,count_pool_count是常量池數組長度+1。比如,假設某個Class文件常量池只有4個元素,那么count_pool_count=5)。

javap解析class文件的時候,常量池的索引從1算起,0默認是給VM自己用得,一般不顯示0這一項。這也是為什么圖3中常量池第一個元素以#1開頭。所以,如果count_pool_count=5的話,真正有用的元素是從count_pool[1]到count_pool[4]。

常量池數組的元素類型由下面的代碼表示:

cp_info { //特別注意,這是介紹的cp_info是相關元素類型的通用表達。

u1 tag; //tag為1個字節長。不論cp_info具體是哪種,第一個字節一定代表tag

u1 info[]; //其他信息,長度隨tag不同而不同

}

//tag取值,先列幾個簡單的:

tag=7 <==info代表這個cp_info是CONSTANT_Class_info結構體

tag=9<==info代表CONSTANT_Fieldrefs_info結構體

tag=10<==info代表CONSTANT_Methodrefs_info結構體

tag=8<==info代表CONSTANT_String_info結構體

tag=1<==info代表CONSTANT_Utf8_info結構體

在JVM規范中,真正代表字符串的數據結構是CONSTANT_Utf8_info結構體,它的結構如下代碼所示:

CONSTANT_Utf8_info {

u1 tag;

u2 length; //下面就是存儲UTF8字符串的地方了

u1 bytes[length];

}

大家看圖3中常量池的內容,比如#2=Utf8 com/test/TestMain 這行表示:

數組第二個元素的類型是CONSTANT_Utf8_info,字符串為“com/test/TestMain”

下面我們看幾個常用的常量池元素類型

(1) CONSTANT_Class_info

這個類型是用于描述類信息的,此處的類信息很簡單,就是類名(也就是代表類名的字符串)

CONSTANT_Class_info {

u1 tag; //tag取值為7,代表CONSTANT_Class_info

u2 name_index; //name_index表示代表自己類名的字符串信息位于于常量池數組中哪一個,也就是索引

}

唉,夠懶的,name_index對應的那個常量池元素必須是CONSTANT_Utf8_info,也就是字符串。圖3中的例子,咱們再看看:

#1 = Class #2 //com/test/TestMain

#2 = Utf8 com/test/TestMain

這說明:

常量池第一個元素類型為Class_info,它對應的name_index取值為2,表示使用第2個元素

常量池第二個元素類型為Utf8 內容為“com/test/TestMain”

#1最后的//表示注釋,它把第二行的字符串內容直接搬過來,方便我們查看

(2) CONSTANT_NameAndType_Info

這個結構也是常量池數據結構中中比較重要的一個,干什么用得呢?恩,它用來描述方法/成員名以及類型信息的。有點JNI基礎的童鞋相信不難明白,在JNI中,一個類的成員函數或成員變量都可以由這個類名字符串+函數名字符串+參數類型字符串+返回值類型來確定(如果是成員變量,就是類名字符串+變量名字符串+類型字符串)來表達。既然是字符串,那么NameAndType_Info也就是存儲了對應字符串在常量池數組中的索引:

CONSTANT_NameAndType_info {

u1 tag;

u2 name_index; //方法名或域名對應的字符串索引

u2 descriptor_index; //方法信息(參數+返回值),或者成員變量的信息(類型)對應的字符串索引

}

//還是來看圖3中的例子吧

#13 = Utf8 ()V

#15 = NameAnType #16.#13 //合起來就是test.()V 函數名是test,參數和返回值是()V

#16=Utf8 test

太簡單了,都不惜得說...,請大家自行解析#25這個常量池元素的內容,一定要做喔!

注意,對于構造函數和類初始化函數來說,JVM要求函數名必須是和。當然,這兩個函數是編譯器生成的。

(3) CONSTANT_MethodrefInfo三兄弟

Methodref_Info還有兩個兄弟,分別是Fieldref_Info,InterfaceMethodref_Info,他們三用于描述方法、成員變量和接口信息。剛才的NameAndType_Info其實已經描述了方法和成員變量信息的一部分,唯一還缺的就是沒有地方描述它們屬于哪個類。而咱這三兄弟就補全了這些信息。他們三的數據結構如圖4所示:

如此直白簡單,不解釋了。不放心的童鞋們請對照圖3的例子自行玩耍!

常量池先介紹到這,它還有一些有用的信息,不過要等到后面我們碰到具體問題時再分析

2.1.2 Field和Method描述

剛才在常量池介紹中有提到Methodref_Info和Fieldref_Info,不過這兩個Info無非是描述了函數或成員變量的名字,參數,類型等信息。但是真正的方法、成員變量信息還包括比如訪問權限,注解,源代碼位置等。對于方法來說,更重要的還包括其函數功能(即這個函數對應的字節碼)。

在Java VM中,方法和成員變量的完整描述由如圖5所示的數據結構來表達的:

access_flags:描述諸如final,static,public這樣的訪問標志

name_index:方法或成員變量名在常量池中對應的索引,類型是Utf8_Info

attribute_info:是域或方法中很重要的信息。我們單獨用一節來介紹它。

2.1.3 attribute_info介紹

attribute_info結構體很簡單,如下代碼所示:

attribute_info {//特別注意,這里描述的attribute_info結構體也是具體屬性數據結構的通用表達

u2 attribute_name_index; //attribute_info的描述,指向常量池的字符串

u4 attribute_length; //具體的內容由info數組描述

u1 info[attribute_length];

}

Java VM規范中,attribute類型比較多,我們重點介紹幾個,先來看代表一個函數實際內容的Code屬性。

(1) Code屬性

代表Code屬性的數據結構如圖6所示:

前2個成員變量就不多說了。屬于attribute的頭6個字節,分別指向代表屬性名字符串的常量池元素以及后續屬性數據的長度。注意,Code屬性的attribute_name_index所指向的那個Utf8常量池元素對應的字符串內容就是“Code”,大家可參考圖3的#9。

max_stack和max_locals:虛擬機在執行一個函數的時候,會為它建立一個操作數棧。執行過程中的參數啊,一些計算值啊等都會壓入棧中。max_stack就表示該函數執行時,這個棧的最大深度。這是編譯時就能確定的。max_locals用于描述這個方法最大的棧數和最大的本地變量個數。本地變量個數包括傳入的參數。

code_length和code:這個函數編譯成Java字節碼后對應的字節碼長度和內容。

exception_table_length:用來描述該方法對應異常處理的信息。這塊我不打算講了,其實也蠻簡單,就是用start_pc表示異常處理時候從此方法對應字節碼(由code[]數組表示)哪個地方開始執行。

Code屬性本身還能包含一些屬性,這是由attributes_count和attributes數組決定的。

來看個實際例子吧,如圖7所示(接著圖3的例子):

圖7中:

stack=2,locals=2,args_size=1。結合代碼,main函數確實有一個參數,而且還有一個本地變量。注意,main函數是static的。如果對于類的非static函數,那么locals的第0個元素代表this。

stack后面接下來的就是code數組,也就是這個函數對應的執行代碼。0表示code[]的索引位置。0:new:代表這個操作是new操作,此操作對應的字節碼長度為3,所以下一個操作對應的字節碼從索引3開始。

LineNumberTable也是屬性的一種,用于調試,它將源碼和字節碼匹配了起來。比如line 7: 0這句話代表該函數字節碼0那一個操作對應代碼的第7行。

LocalVariableTable:它也是屬性一種,用于調試,它用于描述函數執行時的變量信息。比如圖7中的Start = 0:表示從code[]第0個字節開始,Length = 13表示到從start=0到start+13個字節(不包含第13個字節,因為code數組一共就12個字節)這段范圍內,這個變量都有效(也就是這個變量的作用域),Slot=0表示這個變量在本地變量表中第一個元素,還記得前面提到的locals嗎?,name為“args”,表示這個參數的名字叫args,類型(由Signature表示)就是String數組了。

請大家自行解析圖7中最后一行,看看能搞明白LocalVariableTable的含義不...

另外,Android SDK build Tools中的dx工具dump class文件得到的信息更全,大家可以試試。

使用方法是:dx --dump --debug xxx.class。

Class文件先介紹到這,下面我們來看看Android平臺上的dex文件。

2.2 Dex文件結構和Odex

2.2.1 dex文件結構簡介

Android平臺中沒有直接使用Class文件格式,因為早期的Anrdroid手機內存,存儲都比較小,而Class文件顯然有很多可以優化的地方,比如每個Class文件都有一個常量池,里邊存儲了一些字符串。一串內容完全相同的字符串很有可能在不同的Class文件的常量池中存在,這就是一個可以優化的地方。當然,Dex文件結構和Class文件結構差異的地方還很多,但是從攜帶的信息上來看,Dex和Class文件是一致的。所以,你了解了Class文件(作為Java VM官方Spec的標準),Dex文件結構只不過是一個變種罷了(從學習到什么程度為止的問題來看,如果不是要自己來解析Dex文件,或者反編譯/修改dex文件,我覺得大致了解下Dex文件結構的情況就可以了)。圖8所示為Dex文件結構的概貌:

有一點需要說明:傳統Class文件是一個Java源碼文件會生成一個.Class文件,而Android是把所有Class文件進行合并,優化,然后生成一個最終的class.dex,如此,多個Class文件里如果有重復的字符串,當把它們都放到一個dex文件的時候,只要一份就可以了嘛。

dex頭部信息中的magic取值為“dexn035 ”

proto_ids:描述函數原型信息,包括返回值,參數信息。比如“test:()V”

methods_ids:函數信息,包括所屬類及對應的proto信息。比如

"Lcom.test.TestMain. test:()V",.前面是類信息,后面屬于proto信息

下面我們將示例TestMain.class轉換成dex文件,然后再用dexdump工具看看它的結果,如圖9所示:

具體方法:

總結

以上是生活随笔為你收集整理的java dalvik_深入理解Android之Java虚拟机Dalvik的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。