1.JVM與DVM
1.概念
JVM的作用是把平臺(tái)無(wú)關(guān)的.class里面的字節(jié)碼翻譯成平臺(tái)相關(guān)的機(jī)器碼只锻,來(lái)實(shí)現(xiàn)跨平臺(tái)。DVM就是安卓中使用的虛擬機(jī)钠龙。
Dalvik允許多個(gè)實(shí)例炬藤,每一個(gè)實(shí)例作為一個(gè)獨(dú)立的linux進(jìn)程執(zhí)行御铃,可以防止一個(gè)程序的崩潰導(dǎo)致所有程序都崩潰。
2.區(qū)別
- jvm通過(guò)解碼class文件來(lái)運(yùn)行程序沈矿;dvm則是dex文件上真。
dex文件是由多個(gè)class文件打包而成,一個(gè)dex文件方法數(shù)不能超過(guò)65535羹膳。
JVM: .java -> javac -> .class -> jar -> .jar , 架構(gòu): 堆和棧的架構(gòu).
DVM: .java -> javac -> .class -> dx.bat -> .dex , 架構(gòu): 寄存器(cpu上的一塊高速緩存)睡互。
Dalvik字節(jié)碼中,變量會(huì)被復(fù)制給65536個(gè)可用寄存器中的任何一個(gè)陵像,直接訪問(wèn)這些寄存器就珠,而不是方位堆棧中的元素;
JVM字節(jié)碼中醒颖,變量會(huì)被壓入堆棧中進(jìn)行運(yùn)算妻怎;基于寄存器的方式在編譯的時(shí)候花費(fèi)的時(shí)間更短。這也是下面要說(shuō)的泞歉。
- dvm基于寄存器架構(gòu)(句柄引用)逼侦,jvm基于棧架構(gòu)(指針引用)
dvm基于寄存器,所以它的指令是二地址和三地址混合腰耙,指令中指明了操作數(shù)的地址榛丢;jvm基于棧,它的指令是零地址挺庞,指令的操作數(shù)對(duì)象默認(rèn)是操作數(shù)棧中的幾個(gè)位置晰赞。但基于寄存器的指令由于需要指定源地址和目標(biāo)地址,因此需要占用更多的指令空間
dvm速度快选侨!指令數(shù)小掖鱼!寄存器存取速度比棧快的多侵俗,dvm可以根據(jù)硬件實(shí)現(xiàn)最大的優(yōu)化锨用,比較適合移動(dòng)設(shè)備丰刊。JAVA虛擬機(jī)基于棧結(jié)構(gòu)隘谣,程序在運(yùn)行時(shí)虛擬機(jī)需要頻繁的從棧上讀取寫(xiě)入數(shù)據(jù),這個(gè)過(guò)程需要更多的指令分派與內(nèi)存訪問(wèn)次數(shù)啄巧,會(huì)耗費(fèi)很多CPU時(shí)間寻歧。
這樣帶來(lái)的結(jié)果就是dvm的指令數(shù)相對(duì)于jvm的指令數(shù)會(huì)小很多,jvm需要多條指令而dvm可能只需要一條指令秩仆。jvm基于棧帶來(lái)的好處是可以做的足夠簡(jiǎn)單码泛,真正的跨平臺(tái),保證在低硬件條件下能夠正常運(yùn)行澄耍。而dvm操作平臺(tái)一般指明是ARM系統(tǒng)噪珊,所以采取的策略有所不同晌缘。需要注意的是dvm基于寄存器,但是這也是個(gè)映射關(guān)系痢站,如果硬件沒(méi)有足夠的寄存器磷箕,dvm將多出來(lái)的寄存器映射到內(nèi)存中。
- Dalvik可執(zhí)行文件體積更小阵难。
SDK中有個(gè)dx工具負(fù)責(zé)將JAVA字節(jié)碼轉(zhuǎn)換為Dalvik字節(jié)碼岳枷,將所有java文件中的常量池合并為一個(gè)常量池,使得相同的字符串和常量只在DEX文件中出現(xiàn)一次呜叫。
運(yùn)行時(shí)空繁,共享相同的類(lèi),這樣系統(tǒng)消耗會(huì)小很多朱庆,JVM機(jī)制中盛泡,打包后,他們都是完全獨(dú)立的程序娱颊,類(lèi)都是單獨(dú)加載饭于,單獨(dú)運(yùn)行;
3.jvm(參考)
當(dāng)啟動(dòng)一個(gè)java程序维蒙,一個(gè)虛擬機(jī)實(shí)例也就誕生了掰吕。當(dāng)該程序關(guān)閉退出,這個(gè)虛擬機(jī)實(shí)例也就隨之消亡颅痊。
java內(nèi)部有兩種線(xiàn)程:守護(hù)線(xiàn)程和非守護(hù)線(xiàn)程殖熟。非守護(hù)線(xiàn)程就是我們所說(shuō)的main方法;守護(hù)線(xiàn)程通常是由虛擬機(jī)自己使用斑响,如執(zhí)行垃圾回收任務(wù)的線(xiàn)程菱属。Java程序也可以把它創(chuàng)建的任何線(xiàn)程標(biāo)記為守護(hù)線(xiàn)程。只要還有非守護(hù)進(jìn)行在舰罚,這個(gè)java程序也在繼續(xù)進(jìn)行纽门。
每個(gè)Java虛擬機(jī)都有一個(gè)類(lèi)裝載子系統(tǒng),它根據(jù)給定的全限定名來(lái)裝入類(lèi)型(類(lèi)或接口)营罢。同樣赏陵,每個(gè)Java虛擬機(jī)都有一個(gè)執(zhí)行引擎,它負(fù)責(zé)執(zhí)行那些包含在被裝載類(lèi)的方法中的指令饲漾。
每個(gè)Java虛擬機(jī)實(shí)例都有一個(gè)方法區(qū)以及一個(gè)堆蝙搔,它們是由該虛擬機(jī)實(shí)例中所有的線(xiàn)程共享的。當(dāng)虛擬機(jī)裝載一個(gè)class文件時(shí)考传,它會(huì)從這個(gè)class文件包含的二進(jìn)制數(shù)據(jù)中解析類(lèi)型信息吃型。然后把這些類(lèi)型信息放到方法區(qū)中。當(dāng)程序運(yùn)行時(shí)僚楞,虛擬機(jī)會(huì)把所有該程序在運(yùn)行時(shí)創(chuàng)建的對(duì)象都放到堆中勤晚。
當(dāng)每一個(gè)新線(xiàn)程被創(chuàng)建時(shí)枉层,它都將得到它自己的PC寄存器(程序計(jì)數(shù)器)以及一個(gè)Java棧。如果線(xiàn)程正在執(zhí)行的是一個(gè)Java方法(非本地方法)赐写,那么PC寄存器的值將總是指向下一條將被執(zhí)行的指令返干,java棧存儲(chǔ)該線(xiàn)程中Java方法調(diào)用的狀態(tài)——包括它的局部變量,被調(diào)用時(shí)傳進(jìn)來(lái)的參數(shù)血淌、返回值矩欠,以及運(yùn)算的中間結(jié)果等等。而本地方法調(diào)用的狀態(tài)悠夯,則是以某種依賴(lài)于具體實(shí)現(xiàn)的方法存儲(chǔ)在本地方法棧中癌淮,也可能是在寄存器或者其他某些與特定實(shí)現(xiàn)相關(guān)的內(nèi)存區(qū)中。
Java虛擬機(jī)沒(méi)有寄存器沦补,其指令集使用Java棧來(lái)存儲(chǔ)中間數(shù)據(jù)乳蓄。Java棧是由許多棧幀(stack frame)組成的,一個(gè)棧幀包含一個(gè)Java方法調(diào)用的狀態(tài)夕膀。當(dāng)線(xiàn)程調(diào)用一個(gè)Java方法時(shí)虚倒,虛擬機(jī)壓入一個(gè)新的棧幀到該線(xiàn)程的Java棧中,當(dāng)該方法返回時(shí)产舞,這個(gè)棧幀被從Java棧中彈出并拋棄魂奥。
虛擬機(jī)必須為每個(gè)被裝載的類(lèi)型維護(hù)一個(gè)常量池。常量池就是該類(lèi)型所用常量的一個(gè)有序集合易猫,包括直接常量和對(duì)其他類(lèi)型耻煤、字段和方法的符號(hào)引用。池中的數(shù)據(jù)項(xiàng)就像數(shù)組一樣是通過(guò)索引訪問(wèn)的准颓。因?yàn)槌A砍卮鎯?chǔ)了相應(yīng)類(lèi)型所用到的所有類(lèi)型哈蝇、字段和方法的符號(hào)引用,所以它在Java程序的動(dòng)態(tài)連接中起著核心的作用攘已。
創(chuàng)建對(duì)象就是通過(guò)常量池中對(duì)應(yīng)類(lèi)的引用找到方法區(qū)中對(duì)應(yīng)類(lèi)炮赦。
4.Dalvik
Dalvik將堆分成了Active堆和Zygote堆,Zygote堆是Zygote進(jìn)程在啟動(dòng)的時(shí)候預(yù)加載的類(lèi)样勃、資源和對(duì)象吠勘,除此之外的所有對(duì)象都是存儲(chǔ)在Active堆中的。
Dalvik的gygote堆存放的預(yù)加載的類(lèi)都是Android核心類(lèi)和java運(yùn)行時(shí)庫(kù)彤灶,這部分內(nèi)容很少被修改看幼,大多數(shù)情況父進(jìn)程和子進(jìn)程共享這塊內(nèi)存區(qū)域批旺。通常垃圾回收重點(diǎn)對(duì)Active堆進(jìn)行回收操作幌陕,Dalvik為了對(duì)堆進(jìn)行更好的管理創(chuàng)建了一個(gè)Card Table、兩個(gè)Heap Bitmap和一個(gè)Mark Stack數(shù)據(jù)結(jié)構(gòu)汽煮。
許多GC實(shí)現(xiàn)都是在對(duì)象開(kāi)頭的地方留一小塊空間給GC標(biāo)記用搏熄。Dalvik VM則不同棚唆,在進(jìn)行GC的時(shí)候會(huì)單獨(dú)申請(qǐng)一塊空間,以位圖的形式來(lái)保存整個(gè)堆上的對(duì)象的標(biāo)記心例,在GC結(jié)束后就釋放該空間宵凌。
Dalvik使用即時(shí)編譯(JIT),即是在程序運(yùn)行過(guò)程中進(jìn)行編譯止后。JIT會(huì)在運(yùn)行時(shí)分析應(yīng)用程序的代碼瞎惫,識(shí)別哪些方法可以歸類(lèi)為熱方法,這些方法會(huì)被JIT編譯器編譯成對(duì)應(yīng)的匯編代碼译株,然后存儲(chǔ)到代碼緩存中瓜喇,以后調(diào)用這些方法時(shí)就不用解釋執(zhí)行了,可以直接使用代碼緩存中已編譯好的匯編代碼歉糜。
javac把程序源碼編譯成JAVA字節(jié)碼乘寒,JVM通過(guò)逐條解釋字節(jié)碼將其翻譯成對(duì)應(yīng)的機(jī)器指令,逐條讀入匪补,逐條解釋翻譯伞辛,執(zhí)行速度必然比C/C++編譯后的可執(zhí)行二進(jìn)制字節(jié)碼程序慢,為了提高執(zhí)行速度夯缺,就引入了JIT技術(shù)蚤氏。
2.ART
與Dalvik不同,ART使用預(yù)編譯(AOT,Ahead-Of-Time)踊兜。也就是在APK運(yùn)行之前瞧捌,就對(duì)其包含的Dex字節(jié)碼進(jìn)行翻譯,得到對(duì)應(yīng)的本地機(jī)器指令润文,于是就可以在運(yùn)行時(shí)直接執(zhí)行了姐呐。
ART應(yīng)用安裝的時(shí)候把dex中的字節(jié)碼將被編譯成本地機(jī)器碼,之后每次打開(kāi)應(yīng)用典蝌,執(zhí)行的都是本地機(jī)器碼曙砂。去除了運(yùn)行時(shí)的解釋執(zhí)行,效率更高骏掀,啟動(dòng)更快鸠澈。
在Android系統(tǒng)啟動(dòng)過(guò)程中創(chuàng)建的Zygote進(jìn)程利用ART運(yùn)行時(shí)導(dǎo)出的Java虛擬機(jī)接口創(chuàng)建ART虛擬機(jī)。APK在安裝的時(shí)候截驮,打包在里面的classes.dex文件會(huì)被工具dex2oat翻譯成本地機(jī)器指令笑陈,最終得到一個(gè)ELF格式的oat文件。APK運(yùn)行時(shí)葵袭,上述生成的oat文件會(huì)被加載到內(nèi)存中涵妥,并且ART虛擬機(jī)可以通過(guò)里面的oatdata和oatexec段找到任意一個(gè)類(lèi)的方法對(duì)應(yīng)的本地機(jī)器指令來(lái)執(zhí)行。 oat文件中的oatdata包含用來(lái)生成本地機(jī)器指令的dex文件坡锡,內(nèi)容oat文件中的oatexec包含有生成的本地機(jī)器指令蓬网。
可以分配內(nèi)存的Space有三個(gè):Zygote Space窒所、Allocation Space和Large Object Space。實(shí)際上應(yīng)用運(yùn)行的時(shí)候能夠分配內(nèi)存也就Allocation 和 Large Object Space兩個(gè)帆锋。
ART優(yōu)點(diǎn):
①系統(tǒng)性能顯著提升吵取,每次啟動(dòng)執(zhí)行的時(shí)候,都可以直接運(yùn)行锯厢,因此運(yùn)行效率會(huì)提高皮官。
②應(yīng)用啟動(dòng)更快、運(yùn)行更快实辑、體驗(yàn)更流暢臣疑、觸感反饋更及時(shí)
③續(xù)航能力提升,因?yàn)閼?yīng)用程序每次運(yùn)行時(shí)不用重復(fù)編譯了徙菠,從而減少了 CPU 的使用頻率讯沈,降低了能耗。
④支持更低的硬件
ART缺點(diǎn)
①更大的存儲(chǔ)空間占用婿奔,可能增加10%-20%(字節(jié)碼變?yōu)闄C(jī)器碼之后缺狠,可能會(huì)增加10%-20%)
②更長(zhǎng)的應(yīng)用安裝時(shí)間,應(yīng)用在第一次安裝的時(shí)候萍摊,字節(jié)碼就會(huì)預(yù)編譯(AOT)成機(jī)器碼挤茄,這樣的話(huà),雖然設(shè)備和應(yīng)用的首次啟動(dòng)(安裝慢了)會(huì)變慢
4.android7.0\7.1
android7.0\7.1的ART引入了全新的Hybrid模式(Interpreter + JIT + AOT)
- 與前面不同冰木,app在安裝時(shí)不進(jìn)行編譯穷劈,所以安裝速度會(huì)更快。
- 在運(yùn)行App時(shí)踊沸, 先走解釋器歇终, 然后熱點(diǎn)函數(shù)會(huì)被識(shí)別,并被JIT進(jìn)行編譯逼龟, 存儲(chǔ)在jit code cache评凝, 并產(chǎn)生profile文件(記錄熱點(diǎn)函數(shù)信息)。
- 等手機(jī)進(jìn)入充電和空閑狀態(tài)下腺律, 系統(tǒng)會(huì)每隔一段時(shí)間掃描App目錄下profile文件奕短,并執(zhí)行AOT編譯(Google官方稱(chēng)之為profile-guided compilation)。
- 不論是jit編譯的binary code, 還是AOT編譯的binary code, 它們之間的性能差別不大匀钧, 因?yàn)樗鼈兪褂猛粋€(gè)optimizing compiler進(jìn)行編譯翎碑。
這樣的優(yōu)點(diǎn)是:App安裝速度快,占用存儲(chǔ)少(只編譯熱點(diǎn)函數(shù))之斯。
缺點(diǎn)是:前幾次運(yùn)行會(huì)較慢日杈, 只有用戶(hù)操作得次數(shù)越多,jit 和AOT編譯后, 性能才會(huì)跟上來(lái)达椰。
后記
垃圾回收機(jī)制也是很重要的一環(huán)翰蠢,但這個(gè)能說(shuō)的太多了项乒。
參考:
JVM啰劲、Dalvik VM和ART虛擬機(jī)之間的區(qū)別
Dalvik虛擬機(jī)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃