1.Dalvik虛擬機(jī)和Java虛擬機(jī)的區(qū)別
Dalvik虛擬機(jī)使用的是dex(Dalvik Executable)格式的類文件,而Java虛擬機(jī)使用的是class格式的類文件冕房。一個(gè)dex文件可以包含若干個(gè)類肛宋,而一個(gè)class文件只包括一個(gè)類瘟则。由于一個(gè)dex文件可以包含若干個(gè)類,因此它就可以將各個(gè)類中重復(fù)的字符串和其它常數(shù)只保存一次,從而節(jié)省了空間药版,這樣就適合在內(nèi)存和處理器速度有限的手機(jī)系統(tǒng)中使用。
Dalvik虛擬機(jī)使用的指令是基于寄存器的白魂,而Java虛擬機(jī)使用的指令集是基于堆棧的汽纤。
寄存器(Register),是中央處理器內(nèi)的其中組成部分福荸。寄存器是有限存貯容量的高速存貯部件蕴坪,它們可用來暫存指令、數(shù)據(jù)和地址敬锐。在中央處理器的控制部件中背传,包含的寄存器有指令寄存器(IR)和程序計(jì)數(shù)器。在中央處理器的算術(shù)及邏輯部件中台夺,包含的寄存器有累加器径玖。
每一個(gè)Android應(yīng)用在底層都會(huì)對應(yīng)一個(gè)獨(dú)立的Dalvik虛擬機(jī)實(shí)例,其代碼在虛擬機(jī)的解釋器下得以執(zhí)行颤介。
有一個(gè)特殊的虛擬機(jī)進(jìn)程Zygote梳星,他是虛擬機(jī)實(shí)例的孵化器。它在系統(tǒng)啟動(dòng)的時(shí)候就會(huì)產(chǎn)生滚朵,它會(huì)完成虛擬機(jī)的初始化冤灾、庫的加載、預(yù)制類庫和初始化的操作辕近。如果系統(tǒng)需要一個(gè)新的虛擬機(jī)實(shí)例韵吨,它會(huì)迅速復(fù)制自身,以最快的速度提供給系統(tǒng)移宅。對于一些只讀的系統(tǒng)庫归粉,所有虛擬機(jī)實(shí)例都和Zygote共享一塊內(nèi)存區(qū)域。
2.Dalvik的工作流程
Dalvik虛擬機(jī)支持已轉(zhuǎn)換為.dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行吞杭,.dex格式是專為Dalvik設(shè)計(jì)的一種壓縮格式盏浇,適合內(nèi)存和處理器速度有限的系統(tǒng)。(dx 是一套工具芽狗,可以將 Java .class 轉(zhuǎn)換成 .dex 格式. 一個(gè)dex檔通常會(huì)有多個(gè).class绢掰。由于dex有時(shí)必須進(jìn)行最佳化,會(huì)使檔案大小增加1-4倍童擎,以O(shè)DEX結(jié)尾滴劲。)
2.1 java-class
首先讀取源碼,一個(gè)一個(gè)字節(jié)的讀取進(jìn)來顾复,找出來我們Java定義的關(guān)鍵字班挖,比如if ,else,for,while,finally,等這個(gè)步驟就是叫做詞法分析過程
第二步:檢查第一步讀取出來的關(guān)鍵字是否符合Java語言規(guī)范,比如if后面跟的是不是一個(gè)Boolean類型的表達(dá)式芯砸,這個(gè)過程就叫做語法分析
第三步:經(jīng)過以上2個(gè)步驟詞法分析萧芙,語法分析给梅,基本上已經(jīng)按照J(rèn)ava規(guī)范了,接下來就是這些拼裝的代碼要表達(dá)什么意思双揪,也就是語義分析
注:Java源碼中的類名动羽,方法名,變量名渔期,居然都是以字符串形式存儲(chǔ)在常量池中运吓。所以,圖class字節(jié)碼模型中的this_class和super_class分別指向兩個(gè)字符串疯趟,代表本類的名字和基類的名字拘哨。
常量池?cái)?shù)組的元素類型
常量池常見類型:
CONSTANT_Utf8_info:就是字符串
CONSTANT_Class_info:類信息
CONSTANT_NameAndType_Info:用來描述方法/成員名以及類型信息的
Methodref_Info信峻,InterfaceMethodref_Info,F(xiàn)ieldref_Info
用于描述方法姨夹、接口信息和成員變量。
Methodref_Info矾策,InterfaceMethodref_Info,F(xiàn)ieldref_Info數(shù)據(jù)結(jié)構(gòu)
常量池就不說這么多了逃糟,有興趣的就自行解析。解析方法為:javap -verbose xxxx.class
2.2 class-dex
前言:Android平臺(tái)中沒有直接使用Class文件格式蓬豁,是因?yàn)樵缙诘腁nrdroid手機(jī)內(nèi)存绰咽,存儲(chǔ)都比較小,而Class文件顯然有很多可以優(yōu)化的地方不皆,比如每個(gè)Class文件都有一個(gè)常量池,里邊存儲(chǔ)了一些字符串旺聚。一串內(nèi)容完全相同的字符串很有可能在不同的Class文件的常量池中存在眶蕉,這就是一個(gè)可以優(yōu)化的地方造挽。
傳統(tǒng)Class文件是一個(gè)Java源碼文件會(huì)生成一個(gè).Class文件弄痹,而Android是把所有Class文件進(jìn)行合并界酒,優(yōu)化嘴秸,然后生成一個(gè)最終的class.dex岳掐,如此饭耳,多個(gè)Class文件里如果有重復(fù)的字符串,當(dāng)把它們都放到一個(gè)dex文件的時(shí)候纲酗,只要一份就可以了嘛觅赊。
2.3 dex-odex
odex文件就是dex文件具體在某個(gè)系統(tǒng)(不同手機(jī)吮螺,不同手機(jī)的OS帕翻,不同版本的OS等)上的優(yōu)化嘀掸。odex文件的優(yōu)化依賴系統(tǒng)上的幾個(gè)核心模塊(由BOOTCLASSPATH環(huán)境變量給出,一般是/system/framework/下的jar包泉蝌,尤其是core.jar)梨与,主要還是為了提高Dalvik虛擬機(jī)的運(yùn)行速度文狱,這部分內(nèi)容了解即可瞄崇。
3.內(nèi)存管理
3.1物理內(nèi)存
物理內(nèi)存即移動(dòng)設(shè)備上的RAM,當(dāng)啟動(dòng)一個(gè)Android程序時(shí)腮郊,會(huì)啟動(dòng)一個(gè)Dalvik VM進(jìn)程筹燕,系統(tǒng)會(huì)給它分配固定的內(nèi)存空間(16M,32M不定)撒踪,這塊內(nèi)存空間會(huì)映射到RAM上某個(gè)區(qū)域制妄。然后這個(gè)Android程序就會(huì)運(yùn)行在這塊空間上。Java里會(huì)將這塊空間分成Stack棧內(nèi)存和Heap堆內(nèi)存衔掸。stack里存放對象的引用敞映,heap里存放實(shí)際對象數(shù)據(jù)凌埂。
注:android使用了paging與memory-mapping(mmapping)的機(jī)制來管理內(nèi)存瞳抓。這意味著任何你修改的內(nèi)存(無論是通過分配新的對象還是去訪問mmaped pages中的內(nèi)容)都會(huì)貯存在RAM中孩哑,而且不能被paged out。因此唯一完整釋放內(nèi)存的方法是釋放那些你可能hold住的對象的引用胳蛮,當(dāng)這個(gè)對象沒有被任何其他對象所引用的時(shí)候丛晌,它就能夠被GC回收了澎蛛。只有一種例外是:如果系統(tǒng)想要在其他地方重用這個(gè)對象。
3.1.1Java Object Heap
Java Object Heap是用來分配Java對象的桐经,也就是我們在代碼new出來的對象都是位于Java Object Heap上的浙滤。Dalvik虛擬機(jī)在啟動(dòng)的時(shí)候纺腊,可以通過-Xms和-Xmx選項(xiàng)來指定Java Object Heap的最小值和最大值摹菠。可以通過ActivityManager類的成員函數(shù)getMemoryClass來獲得Dalvik虛擬機(jī)的Java Object Heap的最大值。Android應(yīng)用程序進(jìn)程能夠使用的最大內(nèi)存指的是能夠用來分配Java Object的堆煮寡。
3.1.2Bitmap Memory?
它是用來處理圖像的幸撕。在3.0以及更高的版本中外臂,Bitmap Memory就直接是在Java Object Heap中分配了宋光,這樣就可以直接接受GC的管理。
3.1.3Native Heap
Native Heap就是在Native Code中使用malloc等分配出來的內(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)的措施來Kill掉某些進(jìn)程岗屏,用來補(bǔ)充可用內(nèi)存这刷,這樣會(huì)影響系統(tǒng)體驗(yàn)。
4.垃圾收集
Dalvik虛擬機(jī)可以自動(dòng)回收那些不再使用了的Java Object似袁,也就是那些不再被引用了的Java Object昙衅。垃圾自動(dòng)收集機(jī)制將開發(fā)者從內(nèi)存問題中解放出來而涉,極大地提高了開發(fā)效率联予,以及提高了程序的可維護(hù)性沸久。
Dalvik虛擬機(jī)使用Mark-Sweep算法來進(jìn)行垃圾收集麦向。顧名思義,Mark-Sweep算法就是為Mark和Sweep兩個(gè)階段進(jìn)行垃圾回收话告。其中沙郭,Mark階段從根集(Root Set)開始病线,遞歸地標(biāo)記出當(dāng)前所有被引用的對象,而Sweep階段負(fù)責(zé)回收那些沒有被引用的對象绑莺。
當(dāng)Dalvik虛擬機(jī)成功地在堆上分配一個(gè)對象之后纺裁,會(huì)檢查一下當(dāng)前分配的內(nèi)存是否超出一個(gè)閥值欺缘。
GC_FOR_MALLOC:表示是在堆上分配對象時(shí)內(nèi)存不足觸發(fā)的GC谚殊。
GC_CONCURRENT:表示是在已分配內(nèi)存達(dá)到一定量之后觸發(fā)的GC蛤铜。
GC_EXPLICIT:表示是應(yīng)用程序調(diào)用System.gc围肥、VMRuntime.gc接口或者收到SIGUSR1信號時(shí)觸發(fā)的GC虐先。
GC_BEFORE_OOM:表示是在準(zhǔn)備拋OOM異常之前進(jìn)行的最后努力而觸發(fā)的GC派敷。
Dalvik虛擬機(jī)支持非并行和并行兩種GC篮愉。在圖中试躏,左邊是非并行GC的執(zhí)行過程,而右邊是并行GC的執(zhí)行過程泣刹。它們的總體流程是相似的椅您,主要差別在于前者在執(zhí)行的過程中一直是掛起非GC線程的掀泳,而后者是有條件地掛起非GC線程。
第1步到第3步用于并行和非并行GC:
1.? 調(diào)用函數(shù)dvmSuspendAllThreads掛起所有的線程脑沿,以免它們干擾GC庄拇。
2.? 調(diào)用函數(shù)dvmHeapBeginMarkStep初始化Mark Stack丛忆,并且設(shè)定好GC范圍仍秤。
Mark Stack具體來說诗力,當(dāng)我們標(biāo)記完成根集對象之后苇本,就按照它們的地址從小到大的順序標(biāo)記它們所引用的其它對象瓣窄。假設(shè)有A、B裳凸、C和D四個(gè)對象姨谷,它的地址大小關(guān)系為A < B < C < D梦湘,其中捌议,B和D是根集對象禁灼,A被D引用轿曙,C沒有被B和D引用。那么我們將依次遍歷B和D穿铆。當(dāng)遍歷到B的時(shí)候荞雏,沒有發(fā)現(xiàn)它引用其它對象凤优,然后就繼續(xù)向前遍歷D對象蜈彼。發(fā)現(xiàn)它引用了A對象幸逆。按照遞歸的算法还绘,這時(shí)候除了標(biāo)記A對象是正在使用之外拍顷,還應(yīng)該去檢查A對象有沒有引用其它對象昔案,然后又再檢查它引用的對象有沒有又引用其它的對象,一直這樣遍歷下去。這樣就跟函數(shù)遞歸一樣呼伸。更好的做法是將對象A記錄在一個(gè)Mark Stack中括享,然后繼續(xù)檢查地址值比對象D大的其它對象铃辖。對于地址值比對象D大的其它對象娇斩,如果它們引用了一個(gè)地址值比它們小的其它對象犬第,那么這些其它對象同樣要記錄在Mark Stack中。等到該輪檢查結(jié)束之后丰介,再回過頭來檢查記錄在Mark Stack里面的對象哮幢。然后又重復(fù)上述過程橙垢,直到Mark Stack等于空為止钢悲。
3.? 調(diào)用函數(shù)dvmHeapMarkRootSet標(biāo)記根集對象莺琳。
第4到第6步用于并行GC:
4.? 調(diào)用函數(shù)dvmClearCardTable清理Card Table惭等。Card Table由Card組成辞做,一個(gè)Card實(shí)際上就是一個(gè)字節(jié)秤茅,它的值要么是CLEAN框喳,要么是DIRTY厦坛。因?yàn)榻酉聛砦覀儗?huì)喚醒第1步掛起的線程杜秸。并且使用這個(gè)Card Table來記錄那些在GC過程中被修改的對象撬碟。
5.? 調(diào)用函數(shù)dvmUnlock解鎖堆。這個(gè)是針對調(diào)用函數(shù)dvmCollectGarbageInternal執(zhí)行GC前的堆鎖定操作稼钩。
6.? 調(diào)用函數(shù)dvmResumeAllThreads喚醒第1步掛起的線程坝撑。
第7步用于并行和非并行GC:
7.? 調(diào)用函數(shù)dvmHeapScanMarkedObjects從第3步獲得的根集對象開始巡李,歸遞標(biāo)記所有被根集對象引用的對象侨拦。
第8步到第11步用于并行GC:
8.? 調(diào)用函數(shù)dvmLockHeap重新鎖定堆狱从。這個(gè)是針對前面第5步的操作季研。
9.? 調(diào)用函數(shù)dvmSuspendAllThreads重新掛起所有的線程与涡。這個(gè)是針對前面第6步的操作驼卖。
10. 調(diào)用函數(shù)dvmHeapReMarkRootSet更新根集對象酌畜。因?yàn)橛锌赡茉诘?步到第6步的執(zhí)行過程中檩奠,有線程創(chuàng)建了新的根集對象。
11. 調(diào)用函數(shù)dvmHeapReScanMarkedObjects歸遞標(biāo)記那些在第4步到第6步的執(zhí)行過程中被修改的對象蕉扮。這些對象記錄在Card Table中喳钟。
第12步到第14步用于并行和非并行GC:
12. 調(diào)用函數(shù)dvmHeapProcessReferences處理那些被軟引用(Soft Reference)奔则、弱引用(Weak Reference)和影子引用(Phantom Reference)引用的對象易茬,以及重寫了finalize方法的對象抽莱。這些對象都是需要特殊處理的食铐。
13. 調(diào)用函數(shù)dvmHeapSweepSystemWeaks回收系統(tǒng)內(nèi)部使用的那些被弱引用引用的對象虐呻。
14. 調(diào)用函數(shù)dvmHeapSourceSwapBitmaps交換Live Bitmap和Mark Bitmap斟叼。執(zhí)行了前面的13步之后犁柜,所有還被引用的對象在Mark Bitmap中的bit都被設(shè)置為1洲鸠。而Live Bitmap記錄的是當(dāng)前GC前還被引用著的對象。通過交換這兩個(gè)Bitmap馋缅,就可以使得當(dāng)前GC完成之后扒腕,使得Live Bitmap記錄的是下次GC前還被引用著的對象。
第15步和第16步用于并行GC:
15. 調(diào)用函數(shù)dvmUnlock解鎖堆萤悴。這個(gè)是針對前面第8步的操作瘾腰。
16. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第9步掛起的線程。
第17步和第18步用于并行和非并行GC:
17. 調(diào)用函數(shù)dvmHeapSweepUnmarkedObjects回收那些沒有被引用的對象蹋盆。沒有被引用的對象就是那些在執(zhí)行第14步之前,在Live Bitmap中的bit設(shè)置為1硝全,但是在Mark Bitmap中的bit設(shè)置為0的對象栖雾。
18. 調(diào)用函數(shù)dvmHeapFinishMarkStep重置Mark Bitmap以及Mark Stack。這個(gè)是針對前面第2步的操作伟众。
第19步用于并行GC:
19. 調(diào)用函數(shù)dvmLockHeap重新鎖定堆析藕。這個(gè)是針對前面第15步的操作。
第20步用于并行和非并行GC:
20. 調(diào)用函數(shù)dvmHeapSourceGrowForUtilization根據(jù)設(shè)置的堆目標(biāo)利用率調(diào)整堆的大小凳厢。
第21步用于并行GC:
21. 調(diào)用函數(shù)dvmBroadcastCond喚醒那些等待GC執(zhí)行完成再在堆上分配對象的線程账胧。
第22步用于非并行GC:
22. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第1步掛起的線程竞慢。
第23步用到并行和非并行GC:
23. 調(diào)用函數(shù)dvmEnqueueClearedReferences將那些目標(biāo)對象已經(jīng)被回收了的引用對象增加到相應(yīng)的Java隊(duì)列中去,以便應(yīng)用程序可以知道哪些引用引用的對象已經(jīng)被回收了治泥。
5.進(jìn)程與線程管理
Dalvik虛擬機(jī)運(yùn)行在Linux操作系統(tǒng)之上筹煮。我們知道,Linux操作系統(tǒng)并沒有純粹的線程概念居夹,只要兩個(gè)進(jìn)程共享同一個(gè)地址空間败潦,那么就可以認(rèn)為它們同一個(gè)進(jìn)程的兩個(gè)線程。Linux操作系統(tǒng)提供了兩個(gè)fork和clone兩個(gè)調(diào)用吮播,其中变屁,前者就是用來創(chuàng)建進(jìn)程的,而后者就是用來創(chuàng)建線程的意狠。
5.1Thread.start
Thread類的成員函數(shù)start首先檢查成員變量hasBeenStarted的值是否等于true粟关。如果等于true的話,那么就說明當(dāng)前正在處理的Thread對象所描述的Java線程已經(jīng)啟動(dòng)起來了环戈。一個(gè)Java線程是不能重復(fù)啟動(dòng)的闷板,否則的話,Thread類的成員函數(shù)start就會(huì)拋出一個(gè)類型為IllegalThreadStateException的異常院塞。通過了上面的檢查之后遮晚,Thread類的成員函數(shù)start接下來就繼續(xù)調(diào)用VMThread類的靜態(tài)成員函數(shù)create來創(chuàng)建一個(gè)線程。
5.2VMThread.create
VMThread類的靜態(tài)成員函數(shù)create是一個(gè)JNI方法拦止,它將Java層傳遞過來的參數(shù)獲取出來之后县遣,就調(diào)用另外一個(gè)函數(shù)dvmCreateInterpThread來執(zhí)行創(chuàng)建線程的工作。
5.3dvmCreateInterpThread
將用來描述新創(chuàng)建的Dalvik虛擬機(jī)線程的Native層的Thread對象保存在gDvm.threadList所描述的一個(gè)線程列表中汹族,這是因?yàn)楫?dāng)前所有Dalvik虛擬機(jī)線程都保存在這個(gè)列表中萧求。
將新創(chuàng)建的Dalvik虛擬機(jī)線程的狀態(tài)設(shè)置為THREAD_VMWAIT,使得新創(chuàng)建的Dalvik虛擬機(jī)線程繼續(xù)往前執(zhí)行顶瞒,這是因?yàn)樾聞?chuàng)建的Dalvik虛擬機(jī)線程將自己的狀態(tài)設(shè)置為THREAD_STARTING喚醒創(chuàng)建它的線程之后夸政,又會(huì)等待創(chuàng)建它的線程通知它繼續(xù)往前執(zhí)行。
5.4interpThreadStart
1. 調(diào)用函數(shù)prepareThread來初始化新創(chuàng)建的Dalvik虛擬機(jī)線程榴徐。
2. 將新創(chuàng)建的Dalvik虛擬機(jī)線程的狀態(tài)設(shè)置為THREAD_STARTING守问,以便其父線程,也就是創(chuàng)建它的線程可以繼續(xù)往前執(zhí)行坑资。
3. 通過一個(gè)while循環(huán)來等待父線程通知自己繼續(xù)往前執(zhí)行耗帕,也就是等待父線程將自己的狀態(tài)設(shè)置為THREAD_VMWAIT。
4. 調(diào)用函數(shù)dvmCreateJNIEnv來為新創(chuàng)建的Dalvik虛擬機(jī)線程創(chuàng)建一個(gè)JNI環(huán)境袱贮。
5. 調(diào)用函數(shù)dvmChangeStatus將新創(chuàng)建的Dalvik虛擬機(jī)線程的狀態(tài)設(shè)置為THREAD_RUNNING仿便,表示它正式進(jìn)入運(yùn)行狀態(tài)。
6. 如果此時(shí)gDvm.debuggerConnected的值等于true,那么就說明有調(diào)試器連接到當(dāng)前Dalvik虛擬機(jī)來了探越,這時(shí)候就調(diào)用函數(shù)dvmDbgPostThreadStart來通知調(diào)試器新創(chuàng)建了一個(gè)線程。
7. 調(diào)用函數(shù)dvmChangeThreadPriority來設(shè)置新創(chuàng)建的Dalvik虛擬機(jī)線程的優(yōu)先級窑业,這個(gè)優(yōu)先級值保存在用來描述新創(chuàng)建的Dalvik虛擬機(jī)線程的一個(gè)Java層Thread對象的成員變量priority中钦幔。
8. 找到Java層的java.lang.Thread類的成員函數(shù)run,并且通過函數(shù)dvmCallMethod來交給Dalvik虛擬機(jī)解釋器執(zhí)行常柄,這個(gè)java.lang.Thread類的成員函數(shù)run即為Dalvik虛擬機(jī)線程的Java代碼入口點(diǎn)函數(shù)鲤氢。
9. 從函數(shù)dvmCallMethod返回來之后,新創(chuàng)建的Dalvik虛擬機(jī)線程就完成自己的使命了西潘,這時(shí)候就可以調(diào)用函數(shù)dvmDetachCurrentThread來執(zhí)行清理工作卷玉。
6.Dalvik部分源碼分析
jni_invocation.Init:初始化JNI相關(guān)的幾個(gè)重要函數(shù)。通過dlopen加載libdvm.so喷市∠嘀郑看來每個(gè)Java進(jìn)程都會(huì)有這個(gè)東西。這可是dalvik vm的核心庫品姓。這個(gè)庫有很多API寝并,我個(gè)人覺得如果了解libdvm.so的話,應(yīng)該能干很多事情腹备。這里就不講那么仔細(xì)了衬潦。
startVm:注意,它傳入了一個(gè)JNIEnv* env對象進(jìn)去植酥,當(dāng)這個(gè)函數(shù)返回時(shí)镀岛,我們在JNI中天天見的JNIEnv對象就是這個(gè)東西。startVm是Dalvik VM的核心友驮,該函數(shù)返回后漂羊,VM就基本就緒了。其實(shí)startVm方法做的事情就是初始化VM核心數(shù)據(jù)結(jié)構(gòu)喊儡。講一下跟目前有相關(guān)的知識點(diǎn)拨与,實(shí)際上,根據(jù)Java VM規(guī)范艾猜,類的唯一性由全路徑類名+定義它的ClassLoader兩者唯一確定买喧。
startReg:注冊Android平臺(tái)中一些特有的JNI函數(shù)。有興趣的可以深入研究匆赃。
dvmStartup函數(shù)是在startVm函數(shù)內(nèi)的虛擬機(jī)創(chuàng)建的核心淤毛。
dvmStartup首先是解析參數(shù),這些參數(shù)信息可能會(huì)傳給gDvm相關(guān)的成員變量算柳。解析參數(shù)是由setCommandLineDefaults和processOptions來完成的低淡。代碼就不看了,就講幾個(gè)關(guān)鍵參數(shù)。
gDvm.executionMode = kExecutionModeJit:如果定義的WITH_JIT宏蔗蹋,則執(zhí)行模式是JIT模式何荚。
gDvm.bootClassPathStr:由BOOTCLASSPATH環(huán)境變量提供。講一下BOOTCLASSPATH值指向是什么猪杭,system/framework下幾乎所有的jar包都被放在了BOOT CLASSPATH里餐塘。
gDvm.mainThreadStackSize = kDefaultStackSize。kDefaultStackSize值為16K皂吮,代表主線程的堆棧大小
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED戒傻,用于控制odex操作,該參數(shù)表示只對verified的類進(jìn)行odex蜂筹。
接下來一堆startup函數(shù)中的重點(diǎn)需纳,dvmClassStartup函數(shù)
先講一下dvmClassStartup函數(shù)做成了什么事情:
創(chuàng)建了一個(gè)Hash表,用來存儲(chǔ)已經(jīng)加載的類艺挪。
創(chuàng)建了代表java.lang.Class和所有基礎(chǔ)數(shù)據(jù)類型的Class信息不翩。
processClassPath這個(gè)函數(shù),它要加載所有的Boot Class闺属,它涉及到system/framework/下的jar包的加載慌盯,加載完畢后,虛擬機(jī)啟動(dòng)的流程差不多就完了掂器。
接下來講的是Class的加載和初始化:
先調(diào)用dvmDexGetResolvedClass亚皂,看看目標(biāo)類TestAnother是不是已經(jīng)被解析過了。前面曾經(jīng)提到說国瓮,一個(gè)類在初始化的時(shí)候可能會(huì)解析它所使用到的其他類灭必。
假設(shè)被引用的類沒有解析過,則調(diào)用dvmResolveClass來加載目標(biāo)類乃摹。
目標(biāo)類加載成功后禁漓,如果該類沒有初始化過,則調(diào)用dvmInitClass進(jìn)行初始化孵睬。
dvmResolveClass其主要邏輯就是先得到目標(biāo)類名(Lcom/test/TestAnother;)然后調(diào)用dvmFindClassNoInit來加載目標(biāo)類播歼。
dvmFindClassNoInit其主要邏輯就是由于referrer的ClassLoader(也就是使用TestAnother類的TestMain類的ClassLoader)不為空,代碼邏輯將走到findClassFromLoaderNoInit掰读。
findClassFromLoaderNoInit其主要邏輯就是調(diào)用java/lang/ClassLoader的loadClass函數(shù)來加載類秘狞。
加載成功后,接下來就是初始化了蹈集、dvmInitClass從函數(shù)名就能知道它的作用了烁试。
這只是Dalvik虛擬機(jī)學(xué)習(xí)之路的一個(gè)簡易整理版本,如果你想深入學(xué)習(xí)Dalvik虛擬機(jī)拢肆,這些內(nèi)容還是不夠的减响。還有很多東西并沒有講到靖诗,還需要大家繼續(xù)努力。
參考地址: