




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、.:.;Dalvik虛擬機渣滓搜集GC過程分析前面我們分析了Dalvik虛擬機堆的創(chuàng)建過程,以及Java對象在堆上的分配過程。這些知識都是了解Dalvik虛擬機渣滓搜集過程的根底。渣滓搜集是一個復(fù)雜的過程,它要將那些不再被援用的對象進(jìn)展回收。一方面要求Dalvik虛擬機可以標(biāo)志出哪些對象是不再被援用的。另一方面要求Dalvik虛擬機盡快地回收內(nèi)存,防止運用程序長時間停頓。本文就將詳細(xì)分析Dalvik虛擬機是如何處理上述問題完成渣滓搜集過程的。Dalvik虛擬機運用Mark-Sweep算法來進(jìn)展渣滓搜集。顧名思義,Mark-Sweep算法就是為Mark和Sweep兩個階段進(jìn)展渣滓回收。其中,Ma
2、rk階段從根集Root Set開場,遞歸地標(biāo)志出當(dāng)前一切被援用的對象,而Sweep階段擔(dān)任回收那些沒有被援用的對象。在分析Dalvik虛擬機運用的Mark-Sweep算法之前,我們先來了解一下什么情況下會觸發(fā)GC。 Dalvik虛擬機在三種情況下會觸發(fā)四種類型的GC。每一種類型GC運用一個GcSpec構(gòu)造體來描畫,它的定義如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片struct GcSpec /* If true, only the application heap is threatened. */ bool isPartial; /* If tr
3、ue, the trace is run concurrently with the mutator. */ bool isConcurrent; /* Toggles for the soft reference clearing policy. */ bool doPreserve; /* A name for this garbage collection mode. */ const char *reason; ; 這個構(gòu)造體定義在文件dalvik/vm/alloc/Heap.h中。 GcSpec構(gòu)造體的各個成員變量的含義如下所示: isPartial: 為true時,表示僅僅回收Ac
4、tive堆的渣滓;為false時,表示同時回收Active堆和Zygote堆的渣滓。 isConcurrent: 為true時,表示執(zhí)行并行GC;為false時,表示執(zhí)行非并行GC。 doPreserve: 為true時,表示在執(zhí)行GC的過程中,不回收軟援用援用的對象;為false時,表示在執(zhí)行GC的過程中,回收軟援用援用的對象。 reason: 一個描畫性的字符串。 Davlik虛擬機定義了四種類的GC,如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片/* Not enough space for an ordinary Object to be al
5、located. */ extern const GcSpec *GC_FOR_MALLOC; /* Automatic GC triggered by exceeding a heap occupancy threshold. */ extern const GcSpec *GC_CONCURRENT; /* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */ extern const GcSpec *GC_EXPLICIT; /* Final attempt to reclaim memory before throwi
6、ng an OOM. */ extern const GcSpec *GC_BEFORE_OOM; 這四個全局變量聲明在文件dalvik/vm/alloc/Heap.h中。 它們的含義如下所示: GC_FOR_MALLOC: 表示是在堆上分配對象時內(nèi)存缺乏觸發(fā)的GC。 GC_CONCURRENT: 表示是在已分配內(nèi)存到達(dá)一定量之后觸發(fā)的GC。 GC_EXPLICIT: 表示是運用程序調(diào)用System.gc、VMRuntime.gc接口或者收到SIGUSR1信號時觸發(fā)的GC。 GC_BEFORE_OOM: 表示是在預(yù)備拋OOM異常之前進(jìn)展的最后努力而觸發(fā)的GC。 實踐上,GC_FOR_MALLO
7、C、GC_CONCURRENT和GC_BEFORE_OOM三種類型的GC都是在分配對象的過程觸發(fā)的。 在前面一文,我們提到,Dalvik虛擬機在Java堆上分配對象的時候,在碰到分配失敗的情況,會嘗試調(diào)用函數(shù)gcForMalloc進(jìn)展渣滓回收。 函數(shù)gcForMalloc的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片static void gcForMalloc(bool clearSoftReferences) const GcSpec *spec = clearSoftReferences ? GC_BEFORE_OOM : GC_FOR_M
8、ALLOC; dvmCollectGarbageInternal(spec); 這個函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。 參數(shù)clearSOftRefereces表示能否要對軟援用援用的對象進(jìn)展回收。假設(shè)要對軟援用援用的對象進(jìn)展回收,那么就闡明當(dāng)前內(nèi)存是非常緊張的了,因此,這時候執(zhí)行的就是GC_BEFORE_OOM類型的GC。否那么的話,執(zhí)行的就是GC_FOR_MALLOC類型的GC。它們都是經(jīng)過調(diào)用函數(shù)dvmCollectGarbageInternal來執(zhí)行的。 在前面一文,我們也提到,當(dāng)Dalvik虛擬機勝利地在堆上分配一個對象之后,會檢查一下當(dāng)前分配的內(nèi)存能
9、否超出一個閥值,如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void* dvmHeapSourceAlloc(size_t n) HeapSource *hs = gHs; Heap* heap = hs2heap(hs); if (heap-bytesAllocated + n hs-softLimit) return NULL; void* ptr; if (gDvm.lowMemoryMode) ptr = mspace_malloc(heap-msp, n); else ptr = mspace_calloc(heap-msp, 1, n);
10、 countAllocation(heap, ptr); if (heap-bytesAllocated heap-concurrentStartBytes) dvmSignalCond(&gHs-gcThreadCond); return ptr; 這個函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數(shù)dvmHeapSourceAlloc勝利地在Active堆上分配到一個對象之后,就會檢查Active堆當(dāng)前曾經(jīng)分配的內(nèi)存heap-bytesAllocated能否大于預(yù)設(shè)的閥值heap-concurrentStartBytes。假設(shè)大于,那么就會經(jīng)過條件變量g
11、Hs-gcThreadCond喚醒GC線程進(jìn)展渣滓回收。預(yù)設(shè)的閥值heap-concurrentStartBytes是一個比指定的堆最小空閑內(nèi)存小128K的數(shù)值。也就是說,當(dāng)堆的空閑內(nèi)缺乏時,就會觸發(fā)GC_CONCURRENT類型的GC。 GC線程是Dalvik虛擬機啟動的過程中創(chuàng)建的,它的執(zhí)行體函數(shù)是gcDaemonThread,實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片static void *gcDaemonThread(void* arg) dvmChangeStatus(NULL, THREAD_VMWAIT); dvmLockMut
12、ex(&gHs-gcThreadMutex); while (gHs-gcThreadShutdown != true) bool trim = false; if (gHs-gcThreadTrimNeeded) int result = dvmRelativeCondWait(&gHs-gcThreadCond, &gHs-gcThreadMutex, HEAP_TRIM_IDLE_TIME_MS, 0); if (result = ETIMEDOUT) /* Timed out waiting for a GC request, schedule a heap trim. */ trim
13、 = true; else dvmWaitCond(&gHs-gcThreadCond, &gHs-gcThreadMutex); dvmLockHeap(); if (!gDvm.gcHeap-gcRunning) dvmChangeStatus(NULL, THREAD_RUNNING); if (trim) trimHeaps(); gHs-gcThreadTrimNeeded = false; else dvmCollectGarbageInternal(GC_CONCURRENT); gHs-gcThreadTrimNeeded = true; dvmChangeStatus(NUL
14、L, THREAD_VMWAIT); dvmUnlockHeap(); dvmChangeStatus(NULL, THREAD_RUNNING); return NULL; 這個函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 GC線程平常沒事的時候,就在條件變量gHs-gcThreadCond上進(jìn)展等待HEAP_TRIM_IDLE_TIME_MS毫秒5000毫秒。假設(shè)在HEAP_TRIM_IDLE_TIME_MS毫秒內(nèi),都沒有得到執(zhí)行GC的通知,那么它就調(diào)用函數(shù)trimHeaps對Java堆進(jìn)展裁剪,以便可以將堆上的一些沒有運用到的內(nèi)存交還給內(nèi)核。函數(shù)trim
15、Heaps的實現(xiàn)可以參考前面一文。否那么的話,就會調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_CONCURRENT的GC。 留意,函數(shù)gcDaemonThread在調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_CONCURRENT的GC之前,會先調(diào)用函數(shù)dvmLockHeap來鎖定堆的。等到GC執(zhí)行終了,再調(diào)用函數(shù)dvmUnlockHeap來解除對堆的鎖定。這與函數(shù)gcForMalloc調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_FOR_MALLOC和GC_CONCURRENT的GC是一樣的。只不過,對堆進(jìn)
16、展鎖定和解鎖的操作是在調(diào)用堆棧上的函數(shù)dvmMalloc進(jìn)展的,詳細(xì)可以參考前面一文。 當(dāng)運用程序調(diào)用System.gc、VMRuntime.gc接口,或者接納到SIGUSR1信號時,最終會調(diào)用到函數(shù)dvmCollectGarbage,它的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmCollectGarbage() if (gDvm.disableExplicitGc) return; dvmLockHeap(); dvmWaitForConcurrentGcToComplete(); dvmCollectGarbageInter
17、nal(GC_EXPLICIT); dvmUnlockHeap(); 這個函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。 假設(shè)Davik虛擬機在啟動的時候,經(jīng)過-XX:+DisableExplicitGC選項禁用了顯式GC,那么函數(shù)dvmCollectGarbage什么也不做就前往了。這意味著Dalvik虛擬機能夠會不支持運用程序顯式的GC懇求。 一旦Dalvik虛擬機支持顯式GC,那么函數(shù)dvmCollectGarbage就會先鎖定堆,并且等待有能夠正在執(zhí)行的GC_CONCURRENT類型的GC完成之后,再調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類
18、型為GC_EXPLICIT的GC。 以上就是三種情況下會觸發(fā)四種類型的GC。有了這個背景知識之后 ,我們接下來就開場分析Dalvik虛擬機運用的Mark-Sweep算法,整個過程如圖1所示:圖1 Dalvik虛擬機渣滓搜集過程 Dalvik虛擬機支持非并行和并行兩種GC。在圖1中,左邊是非并行GC的執(zhí)行過程,而右邊是并行GC的執(zhí)行過程。它們的總體流程是類似的,主要差別在于前者在執(zhí)行的過程中不斷是掛起非GC線程的,而后者是有條件地掛起非GC線程。 由前面的分析可以知道,無論是并行GC,還是非并行GC,它們都是經(jīng)過函數(shù)dvmCollectGarbageInternal來執(zhí)行的。函數(shù)dvmColle
19、ctGarbageInternal的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmCollectGarbageInternal(const GcSpec* spec) if (gcHeap-gcRunning) return; gcHeap-gcRunning = true; dvmSuspendAllThreads(SUSPEND_FOR_GC); if (!dvmHeapBeginMarkStep(spec-isPartial) dvmAbort(); dvmHeapMarkRootSet(); if (spec-isConcu
20、rrent) dvmClearCardTable(); dvmUnlockHeap(); dvmResumeAllThreads(SUSPEND_FOR_GC); dvmHeapScanMarkedObjects(); if (spec-isConcurrent) dvmLockHeap(); dvmSuspendAllThreads(SUSPEND_FOR_GC); dvmHeapReMarkRootSet(); dvmHeapReScanMarkedObjects(); dvmHeapProcessReferences(&gcHeap-softReferences, spec-doPres
21、erve = false, &gcHeap-weakReferences, &gcHeap-finalizerReferences, &gcHeap-phantomReferences); dvmHeapSweepSystemWeaks(); dvmHeapSourceSwapBitmaps(); if (spec-isConcurrent) dvmUnlockHeap(); dvmResumeAllThreads(SUSPEND_FOR_GC); dvmHeapSweepUnmarkedObjects(spec-isPartial, spec-isConcurrent, &numObject
22、sFreed, &numBytesFreed); dvmHeapFinishMarkStep(); if (spec-isConcurrent) dvmLockHeap(); dvmHeapSourceGrowForUtilization(); gcHeap-gcRunning = false; if (spec-isConcurrent) dvmBroadcastCond(&gDvm.gcHeapCond); if (!spec-isConcurrent) dvmResumeAllThreads(SUSPEND_FOR_GC); dvmEnqueueClearedReferences(&gD
23、vm.gcHeap-clearedReferences); 這個函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。 前面提到,在調(diào)用函數(shù)dvmCollectGarbageInternal之前,堆是曾經(jīng)被鎖定了的,因此這時候任何需求堆上分配對象的線程都會被掛起。留意,這不會影響到那些不需求在堆上分配對象的線程。 在圖1中顯示的GC流程中,除了第一步的Lock Heap和最后一步的Unlock Heap,都對應(yīng)于函數(shù)dvmCollectGarbageInternal的實現(xiàn),它的執(zhí)行邏輯如下所示: 第1步到第3步用于并行和非并行GC: 1. 調(diào)用函數(shù)dvmSuspendAllThre
24、ads掛起一切的線程,以免它們干擾GC。 2. 調(diào)用函數(shù)dvmHeapBeginMarkStep初始化Mark Stack,并且設(shè)定好GC范圍。關(guān)于Mark Stack的引見,可以參考前面一文。 3. 調(diào)用函數(shù)dvmHeapMarkRootSet標(biāo)志根集對象。 第4到第6步用于并行GC: 4. 調(diào)用函數(shù)dvmClearCardTable清理Card Table。關(guān)于Card Table的引見,可以參考前面一文。由于接下來我們將會喚醒第1步掛起的線程。并且運用這個Card Table來記錄那些在GC過程中被修正的對象。 5. 調(diào)用函數(shù)dvmUnlock解鎖堆。這個是針對調(diào)用函數(shù)dvmCollec
25、tGarbageInternal執(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重新鎖定堆。這個是針對前面第5步的操作。 9. 調(diào)用函數(shù)dvmSuspendAllThreads重新掛起一切的線程。這個是針對前面第6步的操作。 10. 調(diào)用函數(shù)dvmHeapReMarkRootSet更新根集對象。由于有能夠在第4步到第6步
26、的執(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)部運用的那些被
27、弱援用援用的對象。 14. 調(diào)用函數(shù)dvmHeapSourceSwapBitmaps交換Live Bitmap和Mark Bitmap。執(zhí)行了前面的13步之后,一切還被援用的對象在Mark Bitmap中的bit都被設(shè)置為1。而Live Bitmap記錄的是當(dāng)前GC前還被援用著的對象。經(jīng)過交換這兩個Bitmap,就可以使得當(dāng)前GC完成之后,使得Live Bitmap記錄的是下次GC前還被援用著的對象。 第15步和第16步用于并行GC: 15. 調(diào)用函數(shù)dvmUnlock解鎖堆。這個是針對前面第8步的操作。 16. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第9步掛起的線程。 第17步和
28、第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。這個是針對前面第2步的操作。 第19步用于并行GC: 19. 調(diào)用函數(shù)dvmLockHeap重新鎖定堆。這個是針對前面第15步的操作。 第20步用于并行和非并行GC: 20. 調(diào)用函數(shù)dvmHeapSourceGrow
29、ForUtilization根據(jù)設(shè)置的堆目的利用率調(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將那些目的對象曾經(jīng)被回收了的援用對象添加到相應(yīng)的Java隊列中去,以便運用程序可以知道哪些援用援用的對象曾經(jīng)被回收了。 以上就是并行和非并行GC的執(zhí)行總體流程,它們的主要區(qū)別在于,前者在GC過程中,有條件地
30、掛起和喚醒非GC線程,而后者在執(zhí)行GC的過程中,不斷都是掛起非GC線程的。并行GC經(jīng)過有條件地掛起和喚醒非GC線程,就可以使得運用程序獲得更好的呼應(yīng)性。但是我們也應(yīng)該看到,并行GC需求多執(zhí)行一次標(biāo)志根集對象以及遞歸標(biāo)志那些在GC過程被訪問了的對象的操作。也就是說,并行GC在運用得運用程序獲得更好的呼應(yīng)性的同時,也需求破費更多的CPU資源。 為了更深化地了解GC的執(zhí)行過程,接下來我們再詳細(xì)分析在上述的23步中涉及到的重要函數(shù)。 1. dvmSuspendAllThreads 函數(shù)dvmSuspendAllThreads用來掛起Dalvik虛擬機中除當(dāng)前線程之外的其它線程,它的實現(xiàn)如下所示:cpp
31、 view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmSuspendAllThreads(SuspendCause why) Thread* self = dvmThreadSelf(); Thread* thread; lockThreadSuspend(susp-all, why); dvmLockThreadList(self); lockThreadSuspendCount(); for (thread = gDvm.threadList; thread != NULL; thread = thread-next) if (thread = self)
32、 continue; /* debugger events dont suspend JDWP thread */ if (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) & thread-handle = dvmJdwpGetDebugThread(gDvm.jdwpState) continue; dvmAddToSuspendCounts(thread, 1, (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) ? 1 : 0); unlockThreadSu
33、spendCount(); for (thread = gDvm.threadList; thread != NULL; thread = thread-next) if (thread = self) continue; /* debugger events dont suspend JDWP thread */ if (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) & thread-handle = dvmJdwpGetDebugThread(gDvm.jdwpState) continue; /* wait for th
34、e other thread to see the pending suspend */ waitForThreadSuspend(self, thread); dvmUnlockThreadList(); unlockThreadSuspend(); 這個函數(shù)定義在文件dalvik/vm/Thread.cpp中。 在以下三種情況下,當(dāng)前線程需求掛起其它線程: 1. 執(zhí)行GC。 2. 調(diào)試器懇求。 3. 發(fā)生調(diào)試事件,如斷點命中。 而且,上述三種情況有能夠是同時發(fā)生的。例如,一個線程在分配對象的過程中發(fā)生GC的同時,調(diào)試器也剛好懇求掛起一切線程。這時候就需求保證每一個掛起操作都是順序執(zhí)行的,即
35、要對掛起線程的操作進(jìn)展加鎖。這是經(jīng)過調(diào)用函數(shù)函數(shù)lockThreadSuspend來實現(xiàn)的。 函數(shù)lockThreadSuspend嘗試獲取gDvm._threadSuspendLock鎖。假設(shè)獲取失敗,就闡明有其它線程也正在懇求掛起Dalvik虛擬機中的線程,包括當(dāng)前線程。每一個Dalvik虛擬機線程都有一個Suspend Count計數(shù),每當(dāng)它們都掛起的時候,對應(yīng)的Suspend Count計數(shù)就會加1,而當(dāng)被喚醒時,對應(yīng)的Suspend Count計數(shù)就會減1。在獲取gDvm._threadSuspendLock鎖失敗的情況下,當(dāng)前線程按照一定的時間間隔檢查本人的Suspend Coun
36、t,直到本人的Suspend Count等于0,并且能勝利獲取gDvm._threadSuspendLock鎖為止。這樣就可以保證每一個掛起Dalvik虛擬機線程的懇求都可以得到順序執(zhí)行。 從函數(shù)lockThreadSuspend前往之后,就闡明當(dāng)前線程可以執(zhí)行掛起其它線程的操作了。它首先要做的第一件事情是遍歷Dalvik虛擬機線程列表,并且調(diào)用函數(shù)dvmAddToSuspendCounts將列表里面的每一個線程對應(yīng)的Suspend Count都添加1,但是除了當(dāng)前線程之外。留意,當(dāng)掛起線程的懇求是調(diào)試器發(fā)出或者是由調(diào)試事件觸發(fā)的時候,Dalvik虛擬機中的JDWP線程是不可以掛起的,由于JD
37、WP線程掛起來之后,就沒法調(diào)試了。在這種情況下,也不能將JDWP線程對應(yīng)的Suspend Count都添加1。 經(jīng)過調(diào)用函數(shù)dvmJdwpGetDebugThread可以獲得JDWP線程句柄,用這個句柄和Dalvik虛擬機線程列表中的線程句柄相比,就可以知道當(dāng)前遍歷的線程能否是JDWP線程。同時,經(jīng)過參數(shù)why可以知道當(dāng)掛起線程的懇求能否是由調(diào)試器發(fā)出的或者由調(diào)試事件觸發(fā)的, 留意,在添加被掛起線程的Suspend Count計數(shù)之前,必需求獲取gDvm.threadSuspendCountLock鎖。這個鎖的獲取和釋放可以經(jīng)過函數(shù)lockThreadSuspendCount和unlockTh
38、readSuspendCount完成。 將被掛起線程的Suspend Count計數(shù)都添加1之后,接下來就是等待被掛起線程自愿將本人掛起來了。這是經(jīng)過函數(shù)waitForThreadSuspend來實現(xiàn)。當(dāng)一個線程自愿將本人掛起來的時候,會將本人的形狀設(shè)置為非運轉(zhuǎn)形狀THREAD_RUNNING,這樣函數(shù)waitForThreadSuspend經(jīng)過不斷地檢查一個線程的形狀能否處于非運轉(zhuǎn)形狀就可以知道它能否曾經(jīng)掛起來了。 那么,一個線程在什么情況才會自愿將本人掛起來呢?一個線程在執(zhí)行的過程中,會在適宜的時候檢查本人的Suspend Count計數(shù)。一旦該計數(shù)值不等于0,那么它就知道有線程懇求掛起本
39、人,因此它就會很配合地將本人的形狀設(shè)置為非運轉(zhuǎn)的,并且將本人掛起來。例如,當(dāng)一個線程經(jīng)過解釋器執(zhí)行代碼時,就會周期性地檢查本人的Suspend Count能否等于0。這里說的周期性,實踐上就是碰到IF指令、GOTO指令、SWITCH指令、RETURN指令和THROW指令等時。 2. dvmHeapBeginMarkStep 函數(shù)dvmHeapBeginMarkStep用來初始化堆的標(biāo)志范圍和Mark Stack,它的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片bool dvmHeapBeginMarkStep(bool isPartial) Gc
40、MarkContext *ctx = &gDvm.gcHeap-markContext; if (!createMarkStack(&ctx-stack) return false; ctx-finger = NULL; ctx-immuneLimit = (char*)dvmHeapSourceGetImmuneLimit(isPartial); return true; 這個函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 在標(biāo)志過程中用到的各種信息都保管一個GcMarkContext構(gòu)造體描畫的GC標(biāo)志上下文ctx中。其中,ctx-stack描畫的是Mark
41、Stack,ctx-finger描畫的是一個標(biāo)志指紋,在遞歸標(biāo)志對象時會用到,ctx-immuneLimit用來限定堆的標(biāo)志范圍。 函數(shù)dvmHeapBeginMarkStep調(diào)用另外一個函數(shù)createMarkStack來初始化Mark Stack,它的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片static bool createMarkStack(GcMarkStack *stack) assert(stack != NULL); size_t length = dvmHeapSourceGetIdealFootprint() * size
42、of(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); madvise(stack-base, length, MADV_NORMAL); stack-top = stack-base; return true; 這個函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 函數(shù)createMarkStack根據(jù)最壞情況來設(shè)置Mark Stack的長度,也就是用當(dāng)前堆的大小除以對象占用的最小內(nèi)存得到的結(jié)果。當(dāng)前堆的大小可以經(jīng)過函數(shù)dvmHeapSourceGetIdealFootprint來獲得。在堆上分配的
43、對象,都是從Object類承繼下來的,因此,每一個對象占用的最小內(nèi)存是sizeof(Object)。由于在為對象分配內(nèi)存時,還需求額外的內(nèi)存來保管管理信息,例照實踐分配給對象的內(nèi)存字節(jié)數(shù)。這些額外的管理信息占用的內(nèi)存字節(jié)數(shù)經(jīng)過宏HEAP_SOURCE_CHUNK_OVERHEAD來描畫。 由于Mark Stack實踐上是一個Object指針數(shù)組,因此有了上面的信息之后,我們就可以計算出Mark Stack的長度length。Mark Stack運用的內(nèi)存塊在Dalvik虛擬機啟動的時候就創(chuàng)建好的,詳細(xì)參考前面一文,起始地址位于stack-base中。由于這塊內(nèi)存開場的時候被函數(shù)madvice設(shè)
44、置為MADV_DONTNEED,因此這里要將它重新設(shè)置為MADV_NORMAL,以便可以通知內(nèi)核,這塊內(nèi)存要開場運用了。 Mark Stack的棧頂stack-top指向的是Mark Stack內(nèi)存塊的起始位置,以后就會從這個位置開場由小到大增長。 回到函數(shù)dvmHeapBeginMarkStep中,ctx-immuneLimit記錄的實踐上是堆的起始標(biāo)志位置。在此位置之前的對象,都不會被GC。這個位置是經(jīng)過函數(shù)dvmHeapSourceGetImmuneLimit來確定的,它的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void *dvmHea
45、pSourceGetImmuneLimit(bool isPartial) if (isPartial) return hs2heap(gHs)-base; else return NULL; 這個函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 當(dāng)參數(shù)isPartial等于true時,函數(shù)dvmHeapSourceGetImmuneLimit前往的是Active堆的起始位置,否那么的話就前往NULL值。也就是說,假設(shè)當(dāng)前執(zhí)行的GC只需求回收部分渣滓,那么就只回收Active堆的渣滓,否那么的話,就同時回收Active堆和Zygote堆的渣滓。 3. dvmHea
46、pMarkRootSet 函數(shù)dvmHeapMarkRootSet用來的標(biāo)志根集對象,它的實現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片/* Mark the set of root objects. * * Things we need to scan: * - System classes defined by root classloader * - For each thread: * - Interpreted stack, from top to curFrame * - Dalvik registers (args + local v
47、ars) * - JNI local references * - Automatic VM local references (TrackedAlloc) * - Associated Thread/VMThread object * - ThreadGroups (could track & start with these instead of working * upward from Threads) * - Exception currently being thrown, if present * - JNI global references * - Interned stri
48、ng table * - Primitive classes * - Special objects * - gDvm.outOfMemoryObj * - Objects in debugger object registry * * Dont need: * - Native stack (for in-progress stuff in the VM) * - The TrackedAlloc stuff watches all native VM references. */ void dvmHeapMarkRootSet() GcHeap *gcHeap = gDvm.gcHeap;
49、 dvmMarkImmuneObjects(gcHeap-markContext.immuneLimit); dvmVisitRoots(rootMarkObjectVisitor, &gcHeap-markContext); 這個函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 經(jīng)過注釋我們可以看到根集對象都包含了哪些對象。總的來說,就是包含兩大類對象。一類是Dalvik虛擬機內(nèi)部運用的全局對象,另一類是運用程序正在運用的對象。前者會維護在內(nèi)部的一些數(shù)據(jù)構(gòu)造中,例如Hash表,后者維護在調(diào)用棧中。對這些根集對象進(jìn)展標(biāo)志都是經(jīng)過函數(shù)dvmVisitRoots和回調(diào)函
50、數(shù)rootMarkObjectVisitor進(jìn)展的。 此外,我們還需求將不在堆回收范圍內(nèi)的對象也看作是根集對象,以便后面可以運用一致的方法來遍歷這兩類對象所援用的其它對象。標(biāo)志不在堆回收范圍內(nèi)的對象是經(jīng)過函數(shù)dvmMarkImmuneObjects來實現(xiàn)的,如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmMarkImmuneObjects(const char *immuneLimit) for (size_t i = 1; i numHeaps; +i) if (gHs-heapsi.base heapsi.limit heapsi.b
51、ase - gHs-liveBits.base); /* Compute the starting offset in the live and mark bits. */ char *src = (char *)(gHs-liveBits.bits + index); char *dst = (char *)(gHs-markBits.bits + index); /* Compute the number of bytes of the live bitmap to copy. */ size_t length = HB_OFFSET_TO_BYTE_INDEX( gHs-heapsi.l
52、imit - gHs-heapsi.base); /* Do the copy. */ memcpy(dst, src, length); /* Make sure max points to the address of the highest set bit. */ if (gHs-markBits.max heapsi.limit) gHs-markBits.max = (uintptr_t)gHs-heapsi.limit; 這個函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 從前面分析的函數(shù)dvmHeapSourceGetImmuneLimit可以知道
53、,參數(shù)immuneList要么是等于Zygote堆的最大地址值,要么是等于NULL。這取決于當(dāng)前GC要執(zhí)行的是全部渣滓回收還是部分渣滓回收。 函數(shù)dvmMarkImmuneObjects是在當(dāng)前GC執(zhí)行之前調(diào)用的,這意味著當(dāng)前存活的對象都標(biāo)志在Live Bitmap中。如今函數(shù)dvmMarkImmuneObjects要做的就是將不在回收范圍內(nèi)的對象的標(biāo)志位從Live Bitmap拷貝到Mark Bitmap去。詳細(xì)做法就是分別遍歷Active堆和Zygote堆,假設(shè)它們處于不回范圍中,那么就對里面的對象在Live Bitmap中對應(yīng)的內(nèi)存塊拷貝到Mark Bitmap的對應(yīng)位置去。 計算一個對
54、象在一個Bitmap的標(biāo)志位所在的內(nèi)存塊是經(jīng)過宏HB_OFFSET_TO_INDEX和HB_OFFSET_TO_BYTE_INDEX來實現(xiàn)的。這兩個宏的詳細(xì)實現(xiàn)可以參考前面一文。 回到函數(shù)dvmHeapMarkRootSet中,我們繼續(xù)分析函數(shù)dvmVisitRoots的實現(xiàn),如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmVisitRoots(RootVisitor *visitor, void *arg) assert(visitor != NULL); visitHashTable(visitor, gDvm.loadedClass
55、es, ROOT_STICKY_CLASS, arg); visitPrimitiveTypes(visitor, arg); if (gDvm.dbgRegistry != NULL) visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg); if (gDvm.literalStrings != NULL) visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg); dvmLockMutex(&gDvm.jniGlobalRefLock);
56、 visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg); dvmUnlockMutex(&gDvm.jniGlobalRefLock); dvmLockMutex(&gDvm.jniPinRefLock); visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg); dvmUnlockMutex(&gDvm.jniPinRefLock); visitThreads(visitor, arg);
57、 (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg); (*visitor)(&gDernalErrorObj, 0, ROOT_VM_INTERNAL, arg); (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg); 這個函數(shù)定義在文件dalvik/vm/alloc/Visit.cpp。 參數(shù)visitor指向的是函數(shù)rootMarkObjectVisitor,它擔(dān)任將根集對象在Mark Bitmap中的位設(shè)置為1。我們后面再分析
58、這個函數(shù)的實現(xiàn)。 以下對象將被視為根集對象: 1. 被加載到Dalvik虛擬機的類對象。這些類對象緩存在gDvm.loadedClasses指向的一個Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 2. 在Dalvik虛擬機內(nèi)部創(chuàng)建的原子類,例如Double、Boolean類等,經(jīng)過函數(shù)visitPrimitiveTypes來遍歷和標(biāo)志。 3. 注冊在調(diào)試器的對象。這些對象保管在gDvm.dbgRegistry指向的一個Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 4. 字符串常量池中的String對象。這些對象保管保管在gDvm.literalStrin
59、gs指向的一個Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 5. 在JNI中創(chuàng)建的全局援用對象所援用的對象。這些被援用對象保管在gDvm.jniGlobalRefTable指向的一個間接援用表中,經(jīng)過函數(shù)visitIndirectRefTable來遍歷和標(biāo)志。 6. 在JNI中經(jīng)過GetStringUTFChars、GetByteArrayElements等接口訪問字符串或者數(shù)組時被Pin住的對象。這些對象保管在gDvm.jniPinRefTable指向的一個援用表中,經(jīng)過函數(shù)visitReferenceTable來遍歷和標(biāo)志。 7. 當(dāng)前位于Davlik虛擬機線程的調(diào)用棧
60、的對象。這些對象記錄在棧幀中,經(jīng)過函數(shù)visitThreads來遍歷和標(biāo)志。 8. 在Dalvik虛擬機內(nèi)部創(chuàng)建的OutOfMemory異常對象,這個對象保管在gDvm.outOfMemoryObj中。 9. 在Dalvik虛擬機內(nèi)部創(chuàng)建的InternalError異常對象,這個對象保管在gDernalErrorObj中。 10. 在Dalvik虛擬機內(nèi)部創(chuàng)建的N oClassDefFoundError異常對象,這個對象保管在gDvm.noClassDefFoundErrorObj中。 在上述這些根集對象中,最復(fù)雜和最難遍歷和標(biāo)志的就是位于Dalvik虛擬機線程棧中的對象,因此接下
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 贈與協(xié)議書歸誰所有
- 住宿房屋安全協(xié)議書
- 洛川公益訴訟協(xié)議書
- 資產(chǎn)置換協(xié)議書模板
- 清算資產(chǎn)協(xié)議書范本
- 內(nèi)燃火車轉(zhuǎn)讓協(xié)議書
- 石材加工銷售協(xié)議書
- 增資擴股協(xié)議書要點
- 事故自愿賠償協(xié)議書
- 垃圾安全生產(chǎn)協(xié)議書
- 血液透析患者心力衰竭的護理
- 江蘇省2021-2022學(xué)年二年級下學(xué)期數(shù)學(xué)期中備考卷一(南京專版)
- TCI 535-2024 鋁合金液態(tài)模鍛模具技術(shù)條件
- 胰島素泵護理管理規(guī)范
- 9.1.1 西亞 第1課時 課件 七年級地理下冊 人教版
- 校外培訓(xùn)機構(gòu)預(yù)收費資金托管協(xié)議書范本
- 2025山東能源集團中級人才庫選拔高頻重點模擬試卷提升(共500題附帶答案詳解)
- 《餐廳托盤的使用技》課件
- 【化學(xué)】化學(xué)與可持續(xù)發(fā)展教學(xué)設(shè)計-2024-2025學(xué)年九年級化學(xué)人教版下冊
- Unit 2 Know your body(說課稿) -2024-2025學(xué)年外研版(三起)(2024)英語三年級下冊
- 6.3.1+平面向量基本定理(教學(xué)課件)-高一數(shù)學(xué)(人教A版2019)
評論
0/150
提交評論