九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Dalvik虛擬機(jī)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃

        我們知道,Android應(yīng)用程序是運(yùn)行在Dalvik虛擬機(jī)里面的,并且每一個(gè)應(yīng)用程序?qū)?yīng)有一個(gè)單獨(dú)的Dalvik虛擬機(jī)實(shí)例。除了指令集和類(lèi)文件格式不同,Dalvik虛擬機(jī)與Java虛擬機(jī)共享有差不多的特性,例如,它們都是解釋執(zhí)行,并且支持即時(shí)編譯(JIT)、垃圾收集(GC)、Java本地方法調(diào)用(JNI)和Java遠(yuǎn)程調(diào)試協(xié)議(JDWP)等。本文對(duì)Dalvik虛擬機(jī)進(jìn)行簡(jiǎn)要介紹,以及制定學(xué)習(xí)計(jì)劃。

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!

        Dalvik虛擬機(jī)是由Dan Bornstein開(kāi)發(fā)的,名字來(lái)源于他的祖先曾經(jīng)居住過(guò)的位于冰島的同名小漁村。Dalvik虛擬機(jī)起源于Apache Harmony項(xiàng)目,后者是由Apache軟件基金會(huì)主導(dǎo)的,目標(biāo)是實(shí)現(xiàn)一個(gè)獨(dú)立的、兼容JDK 5的虛擬機(jī),并根據(jù)Apache License v2發(fā)布。由此可見(jiàn),Dalvik虛擬機(jī)從誕生的那一天開(kāi)始,就和Java有說(shuō)不清理不斷的關(guān)系。

        Dalvik虛擬機(jī)與Java虛擬機(jī)的最顯著區(qū)別是它們分別具有不同的類(lèi)文件格式以及指令集。Dalvik虛擬機(jī)使用的是dex(Dalvik Executable)格式的類(lèi)文件,而Java虛擬機(jī)使用的是class格式的類(lèi)文件。一個(gè)dex文件可以包含若干個(gè)類(lèi),而一個(gè)class文件只包括一個(gè)類(lèi)。由于一個(gè)dex文件可以包含若干個(gè)類(lèi),因此它就可以將各個(gè)類(lèi)中重復(fù)的字符串和其它常數(shù)只保存一次,從而節(jié)省了空間,這樣就適合在內(nèi)存和處理器速度有限的手機(jī)系統(tǒng)中使用。一般來(lái)說(shuō),包含有相同類(lèi)的未壓縮dex文件稍小于一個(gè)已經(jīng)壓縮的jar文件。

        Dalvik虛擬機(jī)使用的指令是基于寄存器的,而Java虛擬機(jī)使用的指令集是基于堆棧的?;诙褩5闹噶詈芫o湊,例如,Java虛擬機(jī)使用的指令只占一個(gè)字節(jié),因而稱為字節(jié)碼?;诩拇嫫鞯闹噶钣捎谛枰付ㄔ吹刂泛湍繕?biāo)地址,因此需要占用更多的指令空間,例如,Dalvik虛擬機(jī)的某些指令需要占用兩個(gè)字節(jié)。基于堆棧和基于寄存器的指令集各有優(yōu)劣,一般而言,執(zhí)行同樣的功能,前者需要更多的指令(主要是load和store指令),而后者需要更多的指令空間。需要更多指令意味著要多占用CPU時(shí)間,而需要更多指令空間意味著指令緩沖(i-cache)更易失效。

        此外,還有一種觀點(diǎn)認(rèn)為,基于堆棧的指令更具可移植性,因?yàn)樗粚?duì)目標(biāo)機(jī)器的寄存器進(jìn)行任何假設(shè)。然而,基于寄存器的指令由于對(duì)目標(biāo)機(jī)器的寄存器進(jìn)行了假設(shè),因此,它更有利于進(jìn)行AOT(ahead-of-time)優(yōu)化。 所謂AOT,就是在解釋語(yǔ)言程序運(yùn)行之前,就先將它編譯成本地機(jī)器語(yǔ)言程序。AOT本質(zhì)上是一種靜態(tài)編譯,它是是相對(duì)于JIT而言的,也就是說(shuō),前者是在程序運(yùn)行前進(jìn)行編譯,而后者是在程序運(yùn)行時(shí)進(jìn)行編譯。運(yùn)行時(shí)編譯意味著可以利用運(yùn)行時(shí)信息來(lái)得到比較靜態(tài)編譯更優(yōu)化的代碼,同時(shí)也意味不能進(jìn)行某些高級(jí)優(yōu)化,因?yàn)閮?yōu)化過(guò)程太耗時(shí)了。另一方面,運(yùn)行前編譯由于不占用程序運(yùn)行時(shí)間,因此,它就可以不計(jì)時(shí)間成本來(lái)優(yōu)化代碼。無(wú)論AOT,還是JIT,最終的目標(biāo)都是將解釋語(yǔ)言編譯為本地機(jī)器語(yǔ)言,而本地機(jī)器語(yǔ)言都是基于寄存器來(lái)執(zhí)行的,因此,在某種程度來(lái)講,基于寄存器的指令更有利于進(jìn)行AOT編譯以及優(yōu)化。

        事實(shí)上,基于寄存器和基于堆棧的指令集之爭(zhēng),就如精簡(jiǎn)指令集(RISC)和復(fù)雜指令集(CISC)之爭(zhēng),誰(shuí)優(yōu)誰(shuí)劣,至今是沒(méi)有定論的。例如,上面提到完成相同的功能,基于堆棧的Java虛擬機(jī)需要更多的指令,因此就會(huì)比基于寄存器的Dalvik虛擬機(jī)慢,然而,在2010年,Oracle在一個(gè)ARM設(shè)備上使用一個(gè)non-graphical Java benchmarks來(lái)對(duì)比Java SE Embedded和Android 2.2的性能,發(fā)現(xiàn)后者比前者慢了2~3倍。上述性能比較結(jié)論以及數(shù)據(jù)可以參考以下兩篇文章:

        1. Virtual Machine Showdown: Stack Versus Registers

        2. Java SE Embedded Performance Versus Android 2.2

        基于寄存器的Dalvik虛擬機(jī)和基于堆棧的Java虛擬機(jī)的更多比較和分析,還可以參考以下文章:

        1. http://en.wikipedia.org/wiki/Dalvik_(software)

        2. http://www.infoq.com/news/2007/11/dalvik

        3. http://www.zhihu.com/question/20207106

        不管結(jié)論如何,Dalvik虛擬機(jī)都在盡最大的努力來(lái)優(yōu)化自身,這些措施包括:

        1. 將多個(gè)類(lèi)文件收集到同一個(gè)dex文件中,以便節(jié)省空間;

        2. 使用只讀的內(nèi)存映射方式加載dex文件,以便可以多進(jìn)程共享dex文件,節(jié)省程序加載時(shí)間;

        3. 提前調(diào)整好字節(jié)序(byte order)和字對(duì)齊(word alignment)方式,使得它們更適合于本地機(jī)器,以便提高指令執(zhí)行速度;

        4. 盡量提前進(jìn)行字節(jié)碼驗(yàn)證(bytecode verification),提高程序的加載速度;

        5. 需要重寫(xiě)字節(jié)碼的優(yōu)化要提前進(jìn)行。

        這些優(yōu)化措施的更具體描述可以參考Dalvik Optimization and Verification With dexopt一文。

        分析完Dalvik虛擬機(jī)和Java虛擬機(jī)的區(qū)別之后,接下來(lái)我們?cè)俸?jiǎn)要分析一下Dalvik虛擬機(jī)的其它特性,包括內(nèi)存管理、垃圾收集、JIT、JNI以及進(jìn)程和線程管理。

        一. 內(nèi)存管理

        Dalvik虛擬機(jī)的內(nèi)存大體上可以分為Java Object Heap、Bitmap Memory和Native Heap三種。

        Java Object Heap是用來(lái)分配Java對(duì)象的,也就是我們?cè)诖anew出來(lái)的對(duì)象都是位于Java Object Heap上的。Dalvik虛擬機(jī)在啟動(dòng)的時(shí)候,可以通過(guò)-Xms和-Xmx選項(xiàng)來(lái)指定Java Object Heap的最小值和最大值。為了避免Dalvik虛擬機(jī)在運(yùn)行的過(guò)程中對(duì)Java Object Heap的大小進(jìn)行調(diào)整而影響性能,我們可以通過(guò)-Xms和-Xmx選項(xiàng)來(lái)將它的最小值和最大值設(shè)置為相等。

        Java Object Heap的最小和最大默認(rèn)值為2M和16M,但是手機(jī)在出廠時(shí),廠商會(huì)根據(jù)手機(jī)的配置情況來(lái)對(duì)其進(jìn)行調(diào)整,例如,G1、Droid、Nexus One和Xoom的Java Object Heap的最大值分別為16M、24M、32M 和48M。我們可以通過(guò)ActivityManager類(lèi)的成員函數(shù)getMemoryClass來(lái)獲得Dalvik虛擬機(jī)的Java Object Heap的最大值。

        ActivityManager類(lèi)的成員函數(shù)getMemoryClass的實(shí)現(xiàn)如下所示:

  1. public class ActivityManager {  
  2.     ......  
  3.   
  4.     /** 
  5.      * Return the approximate per-application memory class of the current 
  6.      * device.  This gives you an idea of how hard a memory limit you should 
  7.      * impose on your application to let the overall system work best.  The 
  8.      * returned value is in megabytes; the baseline Android memory class is 
  9.      * 16 (which happens to be the Java heap limit of those devices); some 
  10.      * device with more memory may return 24 or even higher numbers. 
  11.      */  
  12.     public int getMemoryClass() {  
  13.         return staticGetMemoryClass();  
  14.     }  
  15.   
  16.     /** @hide */  
  17.     static public int staticGetMemoryClass() {  
  18.         // Really brain dead right now -- just take this from the configured  
  19.         // vm heap size, and assume it is in megabytes and thus ends with "m".  
  20.         String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m");  
  21.         return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1));  
  22.     }  
  23.   
  24.     ......  
  25. }  
        這個(gè)函數(shù)定義在文件frameworks/base/core/java/android/app/ActivityManager.java中。

        Dalvik虛擬機(jī)在啟動(dòng)的時(shí)候,就是通過(guò)讀取系統(tǒng)屬性dalvik.vm.heapsize的值來(lái)獲得Java Object Heap的最大值的,而ActivityManager類(lèi)的成員函數(shù)getMemoryClass最終也通過(guò)讀取這個(gè)系統(tǒng)屬性的值來(lái)獲得Java Object Heap的最大值。

        這個(gè)Java Object Heap的最大值也就是我們平時(shí)所說(shuō)的Android應(yīng)用程序進(jìn)程能夠使用的最大內(nèi)存。這里必須要注意的是,Android應(yīng)用程序進(jìn)程能夠使用的最大內(nèi)存指的是能夠用來(lái)分配Java Object的堆。

        Bitmap Memory也稱為External Memory,它是用來(lái)處理圖像的。在HoneyComb之前,Bitmap Memory是在Native Heap中分配的,但是這部分內(nèi)存同樣計(jì)入Java Object Heap中,也就是說(shuō),Bitmap占用的內(nèi)存和Java Object占用的內(nèi)存加起來(lái)不能超過(guò)Java Object Heap的最大值。這就是為什么我們?cè)谡{(diào)用BitmapFactory相關(guān)的接口來(lái)處理大圖像時(shí),會(huì)拋出一個(gè)OutOfMemoryError異常的原因:

  1. java.lang.OutOfMemoryError: bitmap size exceeds VM budget  
        在HoneyComb以及更高的版本中,Bitmap Memory就直接是在Java Object Heap中分配了,這樣就可以直接接受GC的管理。

        Native Heap就是在Native Code中使用malloc等分配出來(lái)的內(nèi)存,這部分內(nèi)存是不受Java Object Heap的大小限制的,也就是它可以自由使用,當(dāng)然它是會(huì)受到系統(tǒng)的限制。但是有一點(diǎn)需要注意的是,不要因?yàn)镹ative Heap可以自由使用就濫用,因?yàn)闉E用Native Heap會(huì)導(dǎo)致系統(tǒng)可用內(nèi)存急劇減少,從而引發(fā)系統(tǒng)采取激進(jìn)的措施來(lái)Kill掉某些進(jìn)程,用來(lái)補(bǔ)充可用內(nèi)存,這樣會(huì)影響系統(tǒng)體驗(yàn)。

        此外,在HoneyComb以及更高的版本中,我們可以在AndroidManifest.xml的application標(biāo)簽中增加一個(gè)值等于“true”的android:largeHeap屬性來(lái)通知Dalvik虛擬機(jī)應(yīng)用程序需要使用較大的Java Object Heap。事實(shí)上,在內(nèi)存受限的手機(jī)上,即使我們將一個(gè)應(yīng)用程序的android:largeHeap屬性設(shè)置為“true”,也是不能增加它可用的Java Object Heap的大小的,而即便是可以通過(guò)這個(gè)屬性來(lái)增大Java Object Heap的大小,一般情況也不應(yīng)該使用該屬性。為了提高系統(tǒng)的整體體驗(yàn),我們需要做的是致力于降低應(yīng)用程序的內(nèi)存需求,而不是增加增加應(yīng)用程序的Java Object Heap的大小,畢竟系統(tǒng)總共可用的內(nèi)存是固定的,一個(gè)應(yīng)用程序用得多了,就意味意其它應(yīng)用程序用得少了。

        二. 垃圾收集(GC)

        Dalvik虛擬機(jī)可以自動(dòng)回收那些不再使用了的Java Object,也就是那些不再被引用了的Java Object。垃圾自動(dòng)收集機(jī)制將開(kāi)發(fā)者從內(nèi)存問(wèn)題中解放出來(lái),極大地提高了開(kāi)發(fā)效率,以及提高了程序的可維護(hù)性。

        我們知道,在C或者C++中,開(kāi)發(fā)者需要手動(dòng)地管理在堆中分配的內(nèi)存,但是這往往導(dǎo)致很多問(wèn)題。例如,內(nèi)存分配之后忘記釋放,造成內(nèi)存泄漏。又如,非法訪問(wèn)那些已經(jīng)釋放了的內(nèi)存,引發(fā)程序崩潰。如果沒(méi)有一個(gè)好的C或者C++應(yīng)用程序開(kāi)發(fā)框架,一般的開(kāi)發(fā)者根本無(wú)法駕馭內(nèi)存問(wèn)題,因?yàn)槌绦虼罅酥?,很容易造成失控。最要命的是,?nèi)存被破壞的時(shí)候,并不一定就是程序崩潰的時(shí)候,它就是一顆不定時(shí)炸彈,說(shuō)不準(zhǔn)什么時(shí)候會(huì)被引爆,因此,查找原因是非常困難的。

        從這里我們也可以推斷出,Android為什么會(huì)選擇Java而不是C/C++來(lái)作來(lái)應(yīng)用程序開(kāi)發(fā)語(yǔ)言,就是為了能夠讓開(kāi)發(fā)遠(yuǎn)離內(nèi)存問(wèn)題,而將精力集中在業(yè)務(wù)上,開(kāi)發(fā)出更多更好的APP來(lái),從而迎頭趕超iOS。當(dāng)然,Android系統(tǒng)內(nèi)存也存在大量的C/C++代碼,這只要考慮性能問(wèn)題,畢竟C/C++程序的運(yùn)行性能整體上還是優(yōu)于運(yùn)行在虛擬機(jī)之上的Java程序的。不過(guò),為了避免出現(xiàn)內(nèi)存問(wèn)題,在Android系統(tǒng)內(nèi)部的C++代碼碼,大量地使用了智能指針來(lái)自動(dòng)管理對(duì)象的生命周期。選擇Java來(lái)作為Android應(yīng)用程序的開(kāi)發(fā)語(yǔ)言,可以說(shuō)是技術(shù)與商業(yè)之間一個(gè)折衷,事實(shí)證明,這種折衷是成功的。

        回到正題,在GingerBread之前,Dalvik虛擬使用的垃圾收集機(jī)制有以下特點(diǎn):

        1. Stop-the-word,也就是垃圾收集線程在執(zhí)行的時(shí)候,其它的線程都停止;

        2. Full heap collection,也就是一次收集完全部的垃圾;

        3. 一次垃圾收集造成的程序中止時(shí)間通常都大于100ms。

        在GingerBread以及更高的版本中,Dalvik虛擬使用的垃圾收集機(jī)制得到了改進(jìn),如下所示:

        1.  Cocurrent,也就是大多數(shù)情況下,垃圾收集線程與其它線程是并發(fā)執(zhí)行的;

        2.  Partial collection,也就是一次可能只收集一部分垃圾;

        3.  一次垃圾收集造成的程序中止時(shí)間通常都小于5ms。

        Dalvik虛擬機(jī)執(zhí)行完成一次垃圾收集之后,我們通??梢钥吹筋?lèi)似以下的日志輸出:

  1. D/dalvikvm(9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms  
        在這一行日志中,GC_CONCURRENT表示GC原因,2049K表示總共回收的內(nèi)存,3571K/9991K表示Java Object Heap統(tǒng)計(jì),即在9991K的Java Object Heap中,有3571K是正在使用的,4703K/5261K表示External Memory統(tǒng)計(jì),即在5261K的External Memory中,有4703K是正在使用的,2ms+2ms表示垃圾收集造成的程序中止時(shí)間。

        三. 即時(shí)編譯(JIT)

        前面提到,JIT是相對(duì)AOT而言的,即JIT是在程序運(yùn)行的過(guò)程中進(jìn)行編譯的,而AOT是在程序運(yùn)行前進(jìn)行編譯的。在程序運(yùn)行的過(guò)程中進(jìn)行編譯既有好處,也有壞處。好處在于可以利用程序的運(yùn)行時(shí)信息來(lái)對(duì)編譯出來(lái)的代碼進(jìn)行優(yōu)化,而壞處在于占用程序的運(yùn)行時(shí)間,也就是說(shuō)不能花太多時(shí)間在代碼編譯和優(yōu)化之上。

        為了解決時(shí)間問(wèn)題,JIT可能只會(huì)選擇那些熱點(diǎn)代碼進(jìn)行編譯或者優(yōu)化。根據(jù)2-8原則,一個(gè)程序80%的時(shí)間可能都是在重復(fù)執(zhí)行20%的代碼。因此,JIT就可以選擇這20%經(jīng)常執(zhí)行的代碼來(lái)進(jìn)行編譯和優(yōu)化。

        為了充分地利用好運(yùn)行時(shí)信息來(lái)優(yōu)化代碼,JIT采用一種激進(jìn)的方法。JIT在編譯代碼的時(shí)候,會(huì)對(duì)程序的運(yùn)行情況進(jìn)行假設(shè),并且按照這種假設(shè)來(lái)對(duì)代碼進(jìn)行優(yōu)化。隨著程序的代碼,如果前面的假設(shè)一直保持成立,那么JIT就什么也不用做,因此就可以提高程序的運(yùn)行性能。一旦前面的假設(shè)不再成立了,那么JIT就需要對(duì)前面編譯優(yōu)化的代碼進(jìn)行調(diào)整,以便適應(yīng)新的情況。這種調(diào)整成本可能是很昂貴的,但是只要假設(shè)不成立的情況很少或者幾乎不會(huì)發(fā)生,那么獲得的好處還是大于壞處的。由于JIT在編譯和優(yōu)化代碼的時(shí)候,對(duì)程序的運(yùn)行情況進(jìn)行了假設(shè),因此,它所采取的激進(jìn)優(yōu)化措施又稱為賭博,即Gambling。

        我們以一個(gè)例子來(lái)說(shuō)明這種Gambling。我們知道,Java的同步原語(yǔ)涉及到Lock和Unlock操作。Lock和Unlock操作是非常耗時(shí)的,而且它們只有在多線程環(huán)境中才真的需要。但是一些同步函數(shù)或者同步代碼,有程序運(yùn)行的時(shí)候,有可能始終都是被單線程執(zhí)行,也就是說(shuō),這些同步函數(shù)或者同步代碼不會(huì)被多線程同時(shí)執(zhí)行。這時(shí)候JIT就可以采取一種Lazy Unlocking機(jī)制。

        當(dāng)一個(gè)線程T1進(jìn)入到一個(gè)同步代碼C時(shí),它還是按照正常的流程來(lái)獲取一個(gè)輕量級(jí)鎖L1,并且線程T1的ID會(huì)記錄在輕量鎖L1上。當(dāng)經(jīng)程T1離開(kāi)同步函數(shù)或者同步代碼時(shí),它并不會(huì)釋放前面獲得的輕量級(jí)鎖L1。當(dāng)線程T1再次進(jìn)入同步代碼C時(shí),它就會(huì)發(fā)現(xiàn)輕量級(jí)鎖L的所有者正是自己,因此,它就可以直接執(zhí)行同步代碼C。這時(shí)候如果另外一個(gè)線程T2也要進(jìn)入同步代碼C,它就會(huì)發(fā)現(xiàn)輕量級(jí)鎖L已經(jīng)被線程T1獲取。在這種情況下,JIT就需要檢查線程T1的調(diào)用堆棧,看看它是否還在執(zhí)行同步代碼C。如果是的話,那么就需要將輕量級(jí)鎖L1轉(zhuǎn)換成一個(gè)重量級(jí)鎖L2,并且將重量級(jí)鎖L2的狀態(tài)設(shè)置為鎖定,然后再讓線程T2在重量級(jí)鎖L2上睡眠。等線程T1執(zhí)行完成同步代碼C之后,它就會(huì)按照正常的流程來(lái)釋放重量級(jí)鎖L2,從而喚醒線程T2來(lái)執(zhí)行同步代碼C。另一方面,如果線程T2在進(jìn)入同步代碼C的時(shí)候,JIT通過(guò)檢查線程T1的調(diào)用堆棧,發(fā)現(xiàn)它已經(jīng)離開(kāi)同步代碼C了,那么它就直接將輕量級(jí)鎖L1的所有者記錄為線程T2,并且讓線程T2執(zhí)行同步代碼C。

        通過(guò)上述的Lazy Unlocking機(jī)制,我們就可以充分地利用程序的運(yùn)行時(shí)信息來(lái)提高程序的執(zhí)行性能,這種優(yōu)化對(duì)于靜態(tài)編譯的語(yǔ)言來(lái)說(shuō),是無(wú)法做到的。從這個(gè)角度來(lái)看,我們就可以說(shuō),靜態(tài)編譯語(yǔ)言(如C++)并不一定比在虛擬機(jī)上執(zhí)行的語(yǔ)言(如Java)快,這是因?yàn)楹笳呖梢杂幸环N強(qiáng)大的武器叫做JIT。

        Dalvik虛擬機(jī)從Android 2.2版本開(kāi)始,才支持JIT,而且是可選的。在編譯Dalvik虛擬機(jī)的時(shí)候,可以通過(guò)WITH_JIT宏來(lái)將JIT也編譯進(jìn)去,而在啟動(dòng)Dalvik虛擬機(jī)的時(shí)候,可以通過(guò)-Xint:jit選項(xiàng)來(lái)開(kāi)啟JIT功能。

        關(guān)于虛擬機(jī)JIT的實(shí)現(xiàn)原理的簡(jiǎn)要介紹,可以進(jìn)一步參考這篇文章:http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html。

        四. Java本地調(diào)用(JNI)

        無(wú)論如何,虛擬機(jī)最終都是運(yùn)行在目標(biāo)機(jī)器之上的,也就是說(shuō),它需要將自己的指令翻譯成目標(biāo)機(jī)器指令來(lái)執(zhí)行,并且有些功能,需要通過(guò)調(diào)用目標(biāo)機(jī)器運(yùn)行的操作系統(tǒng)接口來(lái)完成。這樣就需要有一個(gè)機(jī)制,使得函數(shù)調(diào)用可以從Java層穿越到Native層,也就是C/C++層。這種機(jī)制就稱為Java本地調(diào)用,即JNI。當(dāng)然,我們?cè)趫?zhí)行Native代碼的時(shí)候,有時(shí)候也是需要調(diào)用到Java函數(shù)的,這同樣是可以通過(guò)JNI機(jī)制來(lái)實(shí)現(xiàn)。也就是說(shuō),JNI機(jī)制既支持在Java函數(shù)中調(diào)用C/C++函數(shù),也支持在C/C++函數(shù)中調(diào)用Java函數(shù)。

        事實(shí)上,Dalvik虛擬機(jī)提供的Java運(yùn)行時(shí)庫(kù),大部分都是通過(guò)調(diào)用目標(biāo)機(jī)器操作系統(tǒng)接口來(lái)實(shí)現(xiàn)的,也就是通過(guò)調(diào)用Linux系統(tǒng)接口來(lái)實(shí)現(xiàn)的。例如,當(dāng)我們調(diào)用android.os.Process類(lèi)的成員函數(shù)start來(lái)創(chuàng)建一個(gè)進(jìn)程的時(shí)候,最終會(huì)調(diào)用到Linux系統(tǒng)提供的fork系統(tǒng)調(diào)用來(lái)創(chuàng)建一個(gè)進(jìn)程。

        同時(shí),為了方便開(kāi)發(fā)者使用C/C++語(yǔ)言來(lái)開(kāi)發(fā)應(yīng)用程序,Android官方提供了NDK。通過(guò)NDK,我們就可以使用JNI機(jī)制來(lái)在Java函數(shù)中調(diào)用到C/C++函數(shù)。不過(guò)Android官方是不提倡使用NDK來(lái)開(kāi)發(fā)應(yīng)用程序的,這從它對(duì)NDK的支持遠(yuǎn)遠(yuǎn)不如SDK的支持就可以看得出來(lái)。

        五. 進(jìn)程和線程管理

        一般來(lái)說(shuō),虛擬機(jī)的進(jìn)程和線程都是與目標(biāo)機(jī)器本地操作系統(tǒng)的進(jìn)程和線程一一對(duì)應(yīng)的,這樣做的好處是可以使本地操作系統(tǒng)來(lái)調(diào)度進(jìn)程和線程。進(jìn)程和線程調(diào)度是操作系統(tǒng)的核心模塊,它的實(shí)現(xiàn)是非常復(fù)雜的,特別是考慮到多核的情況,因此,就完全沒(méi)有必要在虛擬機(jī)中提供一個(gè)進(jìn)程和線程庫(kù)。

        Dalvik虛擬機(jī)運(yùn)行在Linux操作系統(tǒng)之上。我們知道,Linux操作系統(tǒng)并沒(méi)有純粹的線程概念,只要兩個(gè)進(jìn)程共享同一個(gè)地址空間,那么就可以認(rèn)為它們同一個(gè)進(jìn)程的兩個(gè)線程。Linux操作系統(tǒng)提供了兩個(gè)fork和clone兩個(gè)調(diào)用,其中,前者就是用來(lái)創(chuàng)建進(jìn)程的,而后者就是用來(lái)創(chuàng)建線程的。關(guān)于Linux操作系統(tǒng)的進(jìn)程和線程的實(shí)現(xiàn),可以參考在前面Android學(xué)習(xí)啟動(dòng)篇一文中提到的經(jīng)典Linux內(nèi)核書(shū)籍。

        關(guān)于Android應(yīng)用程序進(jìn)程,它有兩個(gè)很大的特點(diǎn),下面我們就簡(jiǎn)要介紹一下。

        第一個(gè)特點(diǎn)是每一個(gè)Android應(yīng)用程序進(jìn)程都有一個(gè)Dalvik虛擬機(jī)實(shí)例。這樣做的好處是Android應(yīng)用程序進(jìn)程之間不會(huì)相互影響,也就是說(shuō),一個(gè)Android應(yīng)用程序進(jìn)程的意外中止,不會(huì)影響到其它的Android應(yīng)用程序進(jìn)程的正常運(yùn)行。

        第二個(gè)特點(diǎn)是每一個(gè)Android應(yīng)用程序進(jìn)程都是由一種稱為Zygote的進(jìn)程fork出來(lái)的。Zygote進(jìn)程是由init進(jìn)程啟動(dòng)起來(lái)的,也就是在系統(tǒng)啟動(dòng)的時(shí)候啟動(dòng)的。Zygote進(jìn)程在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建一個(gè)虛擬機(jī)實(shí)例,并且在這個(gè)虛擬機(jī)實(shí)例將所有的Java核心庫(kù)都加載起來(lái)。每當(dāng)Zygote進(jìn)程需要?jiǎng)?chuàng)建一個(gè)Android應(yīng)用程序進(jìn)程的時(shí)候,它就通過(guò)復(fù)制自身來(lái)實(shí)現(xiàn),也就是通過(guò)fork系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)。這些被fork出來(lái)的Android應(yīng)用程序進(jìn)程,一方面是復(fù)制了Zygote進(jìn)程中的虛擬機(jī)實(shí)例,另一方面是與Zygote進(jìn)程共享了同一套Java核心庫(kù)。這樣不僅Android應(yīng)用程序進(jìn)程的創(chuàng)建過(guò)程很快,而且由于所有的Android應(yīng)用程序進(jìn)程都共享同一套Java核心庫(kù)而節(jié)省了內(nèi)存空間。

        關(guān)于Dalvik虛擬機(jī)的特性,我們就簡(jiǎn)要介紹到這里。事實(shí)上,Dalvik虛擬機(jī)和Java虛擬機(jī)的實(shí)現(xiàn)是類(lèi)似的,例如,Dalvik虛擬機(jī)也支持JDWP(Java Debug Wire Protocol)協(xié)議,這樣我們就可以使用DDMS來(lái)調(diào)試運(yùn)行在Dalvik虛擬機(jī)中的進(jìn)程。對(duì)Dalvik虛擬機(jī)的其它特性或者實(shí)現(xiàn)原理有興趣的,建議都可以參考Java虛擬機(jī)的實(shí)現(xiàn),這里提供三本參考書(shū):

        1. Java Virtual Machine Specification (Java SE 7) 

        2. Inside the Java Virtual Machine, Second Edition

        3. Oracle JRockit: The Definitive Guide

        另外,關(guān)于Dalvik虛擬機(jī)的指令集和dex文件格式的介紹,可以參考官方文檔:http://source.android.com/tech/dalvik/index.html。如果對(duì)虛擬機(jī)的實(shí)現(xiàn)原理有興趣的,還可以參考這個(gè)鏈接:http://www.weibo.com/1595248757/zvdusrg15

        在這里,我們學(xué)習(xí)Dalvik虛擬機(jī)的目標(biāo)是打通Java層到C/C++層之間的函數(shù)調(diào)用,從而可以更好地理解Android應(yīng)用程序是如何在Linux內(nèi)核上面運(yùn)行的。為了達(dá)到這個(gè)目的,在接下來(lái)的文章中,我們將關(guān)注以下四個(gè)情景:

        1. Dalvik虛擬機(jī)的啟動(dòng)過(guò)程

        2. Dalvik虛擬機(jī)的運(yùn)行過(guò)程;

        3. JNI函數(shù)的注冊(cè)過(guò)程;

        4. Java進(jìn)程和線程的創(chuàng)建過(guò)程。

        掌握了這四個(gè)情景之后,再結(jié)合前面的所有文章,我們就可以從上到下地打通整個(gè)Android系統(tǒng)了,敬請(qǐng)關(guān)注!

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
android調(diào)試工具DDMS的使用詳解
Android Dalvik虛擬機(jī)初識(shí)
Dalvik虛擬機(jī)簡(jiǎn)介
圖解Android應(yīng)用程序構(gòu)建原理
Android 系統(tǒng)架構(gòu)圖
C#移動(dòng)跨平臺(tái)開(kāi)發(fā)(2)Xamarin移動(dòng)跨平臺(tái)解決方案是如何工作的?
更多類(lèi)似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服