版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、.:.;Dalvik虛擬機(jī)渣滓搜集GC過程分析前面我們分析了Dalvik虛擬機(jī)堆的創(chuàng)建過程,以及Java對(duì)象在堆上的分配過程。這些知識(shí)都是了解Dalvik虛擬機(jī)渣滓搜集過程的根底。渣滓搜集是一個(gè)復(fù)雜的過程,它要將那些不再被援用的對(duì)象進(jìn)展回收。一方面要求Dalvik虛擬機(jī)可以標(biāo)志出哪些對(duì)象是不再被援用的。另一方面要求Dalvik虛擬機(jī)盡快地回收內(nèi)存,防止運(yùn)用程序長(zhǎng)時(shí)間停頓。本文就將詳細(xì)分析Dalvik虛擬機(jī)是如何處理上述問題完成渣滓搜集過程的。Dalvik虛擬機(jī)運(yùn)用Mark-Sweep算法來進(jìn)展渣滓搜集。顧名思義,Mark-Sweep算法就是為Mark和Sweep兩個(gè)階段進(jìn)展渣滓回收。其中,Ma
2、rk階段從根集Root Set開場(chǎng),遞歸地標(biāo)志出當(dāng)前一切被援用的對(duì)象,而Sweep階段擔(dān)任回收那些沒有被援用的對(duì)象。在分析Dalvik虛擬機(jī)運(yùn)用的Mark-Sweep算法之前,我們先來了解一下什么情況下會(huì)觸發(fā)GC。 Dalvik虛擬機(jī)在三種情況下會(huì)觸發(fā)四種類型的GC。每一種類型GC運(yùn)用一個(gè)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è)構(gòu)造體定義在文件dalvik/vm/alloc/Heap.h中。 GcSpec構(gòu)造體的各個(gè)成員變量的含義如下所示: isPartial: 為true時(shí),表示僅僅回收Ac
4、tive堆的渣滓;為false時(shí),表示同時(shí)回收Active堆和Zygote堆的渣滓。 isConcurrent: 為true時(shí),表示執(zhí)行并行GC;為false時(shí),表示執(zhí)行非并行GC。 doPreserve: 為true時(shí),表示在執(zhí)行GC的過程中,不回收軟援用援用的對(duì)象;為false時(shí),表示在執(zhí)行GC的過程中,回收軟援用援用的對(duì)象。 reason: 一個(gè)描畫性的字符串。 Davlik虛擬機(jī)定義了四種類的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; 這四個(gè)全局變量聲明在文件dalvik/vm/alloc/Heap.h中。 它們的含義如下所示: GC_FOR_MALLOC: 表示是在堆上分配對(duì)象時(shí)內(nèi)存缺乏觸發(fā)的GC。 GC_CONCURRENT: 表示是在已分配內(nèi)存到達(dá)一定量之后觸發(fā)的GC。 GC_EXPLICIT: 表示是運(yùn)用程序調(diào)用System.gc、VMRuntime.gc接口或者收到SIGUSR1信號(hào)時(shí)觸發(fā)的GC。 GC_BEFORE_OOM: 表示是在預(yù)備拋OOM異常之前進(jìn)展的最后努力而觸發(fā)的GC。 實(shí)踐上,GC_FOR_MALLO
7、C、GC_CONCURRENT和GC_BEFORE_OOM三種類型的GC都是在分配對(duì)象的過程觸發(fā)的。 在前面一文,我們提到,Dalvik虛擬機(jī)在Java堆上分配對(duì)象的時(shí)候,在碰到分配失敗的情況,會(huì)嘗試調(diào)用函數(shù)gcForMalloc進(jìn)展渣滓回收。 函數(shù)gcForMalloc的實(shí)現(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); 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。 參數(shù)clearSOftRefereces表示能否要對(duì)軟援用援用的對(duì)象進(jìn)展回收。假設(shè)要對(duì)軟援用援用的對(duì)象進(jìn)展回收,那么就闡明當(dāng)前內(nèi)存是非常緊張的了,因此,這時(shí)候執(zhí)行的就是GC_BEFORE_OOM類型的GC。否那么的話,執(zhí)行的就是GC_FOR_MALLOC類型的GC。它們都是經(jīng)過調(diào)用函數(shù)dvmCollectGarbageInternal來執(zhí)行的。 在前面一文,我們也提到,當(dāng)Dalvik虛擬機(jī)勝利地在堆上分配一個(gè)對(duì)象之后,會(huì)檢查一下當(dāng)前分配的內(nèi)存能
9、否超出一個(gè)閥值,如下所示: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; 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數(shù)dvmHeapSourceAlloc勝利地在Active堆上分配到一個(gè)對(duì)象之后,就會(huì)檢查Active堆當(dāng)前曾經(jīng)分配的內(nèi)存heap-bytesAllocated能否大于預(yù)設(shè)的閥值heap-concurrentStartBytes。假設(shè)大于,那么就會(huì)經(jīng)過條件變量g
11、Hs-gcThreadCond喚醒GC線程進(jìn)展渣滓回收。預(yù)設(shè)的閥值heap-concurrentStartBytes是一個(gè)比指定的堆最小空閑內(nèi)存小128K的數(shù)值。也就是說,當(dāng)堆的空閑內(nèi)缺乏時(shí),就會(huì)觸發(fā)GC_CONCURRENT類型的GC。 GC線程是Dalvik虛擬機(jī)啟動(dòng)的過程中創(chuàng)建的,它的執(zhí)行體函數(shù)是gcDaemonThread,實(shí)現(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; 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 GC線程平常沒事的時(shí)候,就在條件變量gHs-gcThreadCond上進(jìn)展等待HEAP_TRIM_IDLE_TIME_MS毫秒5000毫秒。假設(shè)在HEAP_TRIM_IDLE_TIME_MS毫秒內(nèi),都沒有得到執(zhí)行GC的通知,那么它就調(diào)用函數(shù)trimHeaps對(duì)Java堆進(jìn)展裁剪,以便可以將堆上的一些沒有運(yùn)用到的內(nèi)存交還給內(nèi)核。函數(shù)trim
15、Heaps的實(shí)現(xiàn)可以參考前面一文。否那么的話,就會(huì)調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_CONCURRENT的GC。 留意,函數(shù)gcDaemonThread在調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_CONCURRENT的GC之前,會(huì)先調(diào)用函數(shù)dvmLockHeap來鎖定堆的。等到GC執(zhí)行終了,再調(diào)用函數(shù)dvmUnlockHeap來解除對(duì)堆的鎖定。這與函數(shù)gcForMalloc調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類型為GC_FOR_MALLOC和GC_CONCURRENT的GC是一樣的。只不過,對(duì)堆進(jìn)
16、展鎖定和解鎖的操作是在調(diào)用堆棧上的函數(shù)dvmMalloc進(jìn)展的,詳細(xì)可以參考前面一文。 當(dāng)運(yùn)用程序調(diào)用System.gc、VMRuntime.gc接口,或者接納到SIGUSR1信號(hào)時(shí),最終會(huì)調(diào)用到函數(shù)dvmCollectGarbage,它的實(shí)現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void dvmCollectGarbage() if (gDvm.disableExplicitGc) return; dvmLockHeap(); dvmWaitForConcurrentGcToComplete(); dvmCollectGarbageInter
17、nal(GC_EXPLICIT); dvmUnlockHeap(); 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。 假設(shè)Davik虛擬機(jī)在啟動(dòng)的時(shí)候,經(jīng)過-XX:+DisableExplicitGC選項(xiàng)禁用了顯式GC,那么函數(shù)dvmCollectGarbage什么也不做就前往了。這意味著Dalvik虛擬機(jī)能夠會(huì)不支持運(yùn)用程序顯式的GC懇求。 一旦Dalvik虛擬機(jī)支持顯式GC,那么函數(shù)dvmCollectGarbage就會(huì)先鎖定堆,并且等待有能夠正在執(zhí)行的GC_CONCURRENT類型的GC完成之后,再調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)展類
18、型為GC_EXPLICIT的GC。 以上就是三種情況下會(huì)觸發(fā)四種類型的GC。有了這個(gè)背景知識(shí)之后 ,我們接下來就開場(chǎng)分析Dalvik虛擬機(jī)運(yùn)用的Mark-Sweep算法,整個(gè)過程如圖1所示:圖1 Dalvik虛擬機(jī)渣滓搜集過程 Dalvik虛擬機(jī)支持非并行和并行兩種GC。在圖1中,左邊是非并行GC的執(zhí)行過程,而右邊是并行GC的執(zhí)行過程。它們的總體流程是類似的,主要差別在于前者在執(zhí)行的過程中不斷是掛起非GC線程的,而后者是有條件地掛起非GC線程。 由前面的分析可以知道,無論是并行GC,還是非并行GC,它們都是經(jīng)過函數(shù)dvmCollectGarbageInternal來執(zhí)行的。函數(shù)dvmColle
19、ctGarbageInternal的實(shí)現(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); 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。 前面提到,在調(diào)用函數(shù)dvmCollectGarbageInternal之前,堆是曾經(jīng)被鎖定了的,因此這時(shí)候任何需求堆上分配對(duì)象的線程都會(huì)被掛起。留意,這不會(huì)影響到那些不需求在堆上分配對(duì)象的線程。 在圖1中顯示的GC流程中,除了第一步的Lock Heap和最后一步的Unlock Heap,都對(duì)應(yīng)于函數(shù)dvmCollectGarbageInternal的實(shí)現(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)志根集對(duì)象。 第4到第6步用于并行GC: 4. 調(diào)用函數(shù)dvmClearCardTable清理Card Table。關(guān)于Card Table的引見,可以參考前面一文。由于接下來我們將會(huì)喚醒第1步掛起的線程。并且運(yùn)用這個(gè)Card Table來記錄那些在GC過程中被修正的對(duì)象。 5. 調(diào)用函數(shù)dvmUnlock解鎖堆。這個(gè)是針對(duì)調(diào)用函數(shù)dvmCollec
25、tGarbageInternal執(zhí)行GC前的堆鎖定操作。 6. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第1步掛起的線程。 第7步用于并行和非并行GC: 7. 調(diào)用函數(shù)dvmHeapScanMarkedObjects從第3步獲得的根集對(duì)象開場(chǎng),歸遞標(biāo)志一切被根集對(duì)象援用的對(duì)象。 第8步到第11步用于并行GC: 8. 調(diào)用函數(shù)dvmLockHeap重新鎖定堆。這個(gè)是針對(duì)前面第5步的操作。 9. 調(diào)用函數(shù)dvmSuspendAllThreads重新掛起一切的線程。這個(gè)是針對(duì)前面第6步的操作。 10. 調(diào)用函數(shù)dvmHeapReMarkRootSet更新根集對(duì)象。由于有能夠在第4步到第6步
26、的執(zhí)行過程中,有線程創(chuàng)建了新的根集對(duì)象。 11. 調(diào)用函數(shù)dvmHeapReScanMarkedObjects歸遞標(biāo)志那些在第4步到第6步的執(zhí)行過程中被修正的對(duì)象。這些對(duì)象記錄在Card Table中。 第12步到第14步用于并行和非并行GC: 12. 調(diào)用函數(shù)dvmHeapProcessReferences處置那些被軟援用Soft Reference、弱援用Weak Reference和影子援用Phantom Reference援用的對(duì)象,以及重寫了finalize方法的對(duì)象。這些對(duì)象都是需求特殊處置的。 13. 調(diào)用函數(shù)dvmHeapSweepSystemWeaks回收系統(tǒng)內(nèi)部運(yùn)用的那些被
27、弱援用援用的對(duì)象。 14. 調(diào)用函數(shù)dvmHeapSourceSwapBitmaps交換Live Bitmap和Mark Bitmap。執(zhí)行了前面的13步之后,一切還被援用的對(duì)象在Mark Bitmap中的bit都被設(shè)置為1。而Live Bitmap記錄的是當(dāng)前GC前還被援用著的對(duì)象。經(jīng)過交換這兩個(gè)Bitmap,就可以使得當(dāng)前GC完成之后,使得Live Bitmap記錄的是下次GC前還被援用著的對(duì)象。 第15步和第16步用于并行GC: 15. 調(diào)用函數(shù)dvmUnlock解鎖堆。這個(gè)是針對(duì)前面第8步的操作。 16. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第9步掛起的線程。 第17步和
28、第18步用于并行和非并行GC: 17. 調(diào)用函數(shù)dvmHeapSweepUnmarkedObjects回收那些沒有被援用的對(duì)象。沒有被援用的對(duì)象就是那些在執(zhí)行第14步之前,在Live Bitmap中的bit設(shè)置為1,但是在Mark Bitmap中的bit設(shè)置為0的對(duì)象。 18. 調(diào)用函數(shù)dvmHeapFinishMarkStep重置Mark Bitmap以及Mark Stack。這個(gè)是針對(duì)前面第2步的操作。 第19步用于并行GC: 19. 調(diào)用函數(shù)dvmLockHeap重新鎖定堆。這個(gè)是針對(duì)前面第15步的操作。 第20步用于并行和非并行GC: 20. 調(diào)用函數(shù)dvmHeapSourceGrow
29、ForUtilization根據(jù)設(shè)置的堆目的利用率調(diào)整堆的大小。 第21步用于并行GC: 21. 調(diào)用函數(shù)dvmBroadcastCond喚醒那些等待GC執(zhí)行完成再在堆上分配對(duì)象的線程。 第22步用于非并行GC: 22. 調(diào)用函數(shù)dvmResumeAllThreads喚醒第1步掛起的線程。 第23步用到并行和非并行GC: 23. 調(diào)用函數(shù)dvmEnqueueClearedReferences將那些目的對(duì)象曾經(jīng)被回收了的援用對(duì)象添加到相應(yīng)的Java隊(duì)列中去,以便運(yùn)用程序可以知道哪些援用援用的對(duì)象曾經(jīng)被回收了。 以上就是并行和非并行GC的執(zhí)行總體流程,它們的主要區(qū)別在于,前者在GC過程中,有條件地
30、掛起和喚醒非GC線程,而后者在執(zhí)行GC的過程中,不斷都是掛起非GC線程的。并行GC經(jīng)過有條件地掛起和喚醒非GC線程,就可以使得運(yùn)用程序獲得更好的呼應(yīng)性。但是我們也應(yīng)該看到,并行GC需求多執(zhí)行一次標(biāo)志根集對(duì)象以及遞歸標(biāo)志那些在GC過程被訪問了的對(duì)象的操作。也就是說,并行GC在運(yùn)用得運(yùn)用程序獲得更好的呼應(yīng)性的同時(shí),也需求破費(fèi)更多的CPU資源。 為了更深化地了解GC的執(zhí)行過程,接下來我們?cè)僭敿?xì)分析在上述的23步中涉及到的重要函數(shù)。 1. dvmSuspendAllThreads 函數(shù)dvmSuspendAllThreads用來掛起Dalvik虛擬機(jī)中除當(dāng)前線程之外的其它線程,它的實(shí)現(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(); 這個(gè)函數(shù)定義在文件dalvik/vm/Thread.cpp中。 在以下三種情況下,當(dāng)前線程需求掛起其它線程: 1. 執(zhí)行GC。 2. 調(diào)試器懇求。 3. 發(fā)生調(diào)試事件,如斷點(diǎn)命中。 而且,上述三種情況有能夠是同時(shí)發(fā)生的。例如,一個(gè)線程在分配對(duì)象的過程中發(fā)生GC的同時(shí),調(diào)試器也剛好懇求掛起一切線程。這時(shí)候就需求保證每一個(gè)掛起操作都是順序執(zhí)行的,即
35、要對(duì)掛起線程的操作進(jìn)展加鎖。這是經(jīng)過調(diào)用函數(shù)函數(shù)lockThreadSuspend來實(shí)現(xiàn)的。 函數(shù)lockThreadSuspend嘗試獲取gDvm._threadSuspendLock鎖。假設(shè)獲取失敗,就闡明有其它線程也正在懇求掛起Dalvik虛擬機(jī)中的線程,包括當(dāng)前線程。每一個(gè)Dalvik虛擬機(jī)線程都有一個(gè)Suspend Count計(jì)數(shù),每當(dāng)它們都掛起的時(shí)候,對(duì)應(yīng)的Suspend Count計(jì)數(shù)就會(huì)加1,而當(dāng)被喚醒時(shí),對(duì)應(yīng)的Suspend Count計(jì)數(shù)就會(huì)減1。在獲取gDvm._threadSuspendLock鎖失敗的情況下,當(dāng)前線程按照一定的時(shí)間間隔檢查本人的Suspend Coun
36、t,直到本人的Suspend Count等于0,并且能勝利獲取gDvm._threadSuspendLock鎖為止。這樣就可以保證每一個(gè)掛起Dalvik虛擬機(jī)線程的懇求都可以得到順序執(zhí)行。 從函數(shù)lockThreadSuspend前往之后,就闡明當(dāng)前線程可以執(zhí)行掛起其它線程的操作了。它首先要做的第一件事情是遍歷Dalvik虛擬機(jī)線程列表,并且調(diào)用函數(shù)dvmAddToSuspendCounts將列表里面的每一個(gè)線程對(duì)應(yīng)的Suspend Count都添加1,但是除了當(dāng)前線程之外。留意,當(dāng)掛起線程的懇求是調(diào)試器發(fā)出或者是由調(diào)試事件觸發(fā)的時(shí)候,Dalvik虛擬機(jī)中的JDWP線程是不可以掛起的,由于JD
37、WP線程掛起來之后,就沒法調(diào)試了。在這種情況下,也不能將JDWP線程對(duì)應(yīng)的Suspend Count都添加1。 經(jīng)過調(diào)用函數(shù)dvmJdwpGetDebugThread可以獲得JDWP線程句柄,用這個(gè)句柄和Dalvik虛擬機(jī)線程列表中的線程句柄相比,就可以知道當(dāng)前遍歷的線程能否是JDWP線程。同時(shí),經(jīng)過參數(shù)why可以知道當(dāng)掛起線程的懇求能否是由調(diào)試器發(fā)出的或者由調(diào)試事件觸發(fā)的, 留意,在添加被掛起線程的Suspend Count計(jì)數(shù)之前,必需求獲取gDvm.threadSuspendCountLock鎖。這個(gè)鎖的獲取和釋放可以經(jīng)過函數(shù)lockThreadSuspendCount和unlockTh
38、readSuspendCount完成。 將被掛起線程的Suspend Count計(jì)數(shù)都添加1之后,接下來就是等待被掛起線程自愿將本人掛起來了。這是經(jīng)過函數(shù)waitForThreadSuspend來實(shí)現(xiàn)。當(dāng)一個(gè)線程自愿將本人掛起來的時(shí)候,會(huì)將本人的形狀設(shè)置為非運(yùn)轉(zhuǎn)形狀THREAD_RUNNING,這樣函數(shù)waitForThreadSuspend經(jīng)過不斷地檢查一個(gè)線程的形狀能否處于非運(yùn)轉(zhuǎn)形狀就可以知道它能否曾經(jīng)掛起來了。 那么,一個(gè)線程在什么情況才會(huì)自愿將本人掛起來呢?一個(gè)線程在執(zhí)行的過程中,會(huì)在適宜的時(shí)候檢查本人的Suspend Count計(jì)數(shù)。一旦該計(jì)數(shù)值不等于0,那么它就知道有線程懇求掛起本
39、人,因此它就會(huì)很配合地將本人的形狀設(shè)置為非運(yùn)轉(zhuǎn)的,并且將本人掛起來。例如,當(dāng)一個(gè)線程經(jīng)過解釋器執(zhí)行代碼時(shí),就會(huì)周期性地檢查本人的Suspend Count能否等于0。這里說的周期性,實(shí)踐上就是碰到IF指令、GOTO指令、SWITCH指令、RETURN指令和THROW指令等時(shí)。 2. dvmHeapBeginMarkStep 函數(shù)dvmHeapBeginMarkStep用來初始化堆的標(biāo)志范圍和Mark Stack,它的實(shí)現(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; 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 在標(biāo)志過程中用到的各種信息都保管一個(gè)GcMarkContext構(gòu)造體描畫的GC標(biāo)志上下文ctx中。其中,ctx-stack描畫的是Mark
41、Stack,ctx-finger描畫的是一個(gè)標(biāo)志指紋,在遞歸標(biāo)志對(duì)象時(shí)會(huì)用到,ctx-immuneLimit用來限定堆的標(biāo)志范圍。 函數(shù)dvmHeapBeginMarkStep調(diào)用另外一個(gè)函數(shù)createMarkStack來初始化Mark Stack,它的實(shí)現(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; 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 函數(shù)createMarkStack根據(jù)最壞情況來設(shè)置Mark Stack的長(zhǎng)度,也就是用當(dāng)前堆的大小除以對(duì)象占用的最小內(nèi)存得到的結(jié)果。當(dāng)前堆的大小可以經(jīng)過函數(shù)dvmHeapSourceGetIdealFootprint來獲得。在堆上分配的
43、對(duì)象,都是從Object類承繼下來的,因此,每一個(gè)對(duì)象占用的最小內(nèi)存是sizeof(Object)。由于在為對(duì)象分配內(nèi)存時(shí),還需求額外的內(nèi)存來保管管理信息,例照實(shí)踐分配給對(duì)象的內(nèi)存字節(jié)數(shù)。這些額外的管理信息占用的內(nèi)存字節(jié)數(shù)經(jīng)過宏HEAP_SOURCE_CHUNK_OVERHEAD來描畫。 由于Mark Stack實(shí)踐上是一個(gè)Object指針數(shù)組,因此有了上面的信息之后,我們就可以計(jì)算出Mark Stack的長(zhǎng)度length。Mark Stack運(yùn)用的內(nèi)存塊在Dalvik虛擬機(jī)啟動(dòng)的時(shí)候就創(chuàng)建好的,詳細(xì)參考前面一文,起始地址位于stack-base中。由于這塊內(nèi)存開場(chǎng)的時(shí)候被函數(shù)madvice設(shè)
44、置為MADV_DONTNEED,因此這里要將它重新設(shè)置為MADV_NORMAL,以便可以通知內(nèi)核,這塊內(nèi)存要開場(chǎng)運(yùn)用了。 Mark Stack的棧頂stack-top指向的是Mark Stack內(nèi)存塊的起始位置,以后就會(huì)從這個(gè)位置開場(chǎng)由小到大增長(zhǎng)。 回到函數(shù)dvmHeapBeginMarkStep中,ctx-immuneLimit記錄的實(shí)踐上是堆的起始標(biāo)志位置。在此位置之前的對(duì)象,都不會(huì)被GC。這個(gè)位置是經(jīng)過函數(shù)dvmHeapSourceGetImmuneLimit來確定的,它的實(shí)現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void *dvmHea
45、pSourceGetImmuneLimit(bool isPartial) if (isPartial) return hs2heap(gHs)-base; else return NULL; 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。 當(dāng)參數(shù)isPartial等于true時(shí),函數(shù)dvmHeapSourceGetImmuneLimit前往的是Active堆的起始位置,否那么的話就前往NULL值。也就是說,假設(shè)當(dāng)前執(zhí)行的GC只需求回收部分渣滓,那么就只回收Active堆的渣滓,否那么的話,就同時(shí)回收Active堆和Zygote堆的渣滓。 3. dvmHea
46、pMarkRootSet 函數(shù)dvmHeapMarkRootSet用來的標(biāo)志根集對(duì)象,它的實(shí)現(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); 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。 經(jīng)過注釋我們可以看到根集對(duì)象都包含了哪些對(duì)象??偟膩碚f,就是包含兩大類對(duì)象。一類是Dalvik虛擬機(jī)內(nèi)部運(yùn)用的全局對(duì)象,另一類是運(yùn)用程序正在運(yùn)用的對(duì)象。前者會(huì)維護(hù)在內(nèi)部的一些數(shù)據(jù)構(gòu)造中,例如Hash表,后者維護(hù)在調(diào)用棧中。對(duì)這些根集對(duì)象進(jìn)展標(biāo)志都是經(jīng)過函數(shù)dvmVisitRoots和回調(diào)函
50、數(shù)rootMarkObjectVisitor進(jìn)展的。 此外,我們還需求將不在堆回收范圍內(nèi)的對(duì)象也看作是根集對(duì)象,以便后面可以運(yùn)用一致的方法來遍歷這兩類對(duì)象所援用的其它對(duì)象。標(biāo)志不在堆回收范圍內(nèi)的對(duì)象是經(jīng)過函數(shù)dvmMarkImmuneObjects來實(shí)現(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; 這個(gè)函數(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)前存活的對(duì)象都標(biāo)志在Live Bitmap中。如今函數(shù)dvmMarkImmuneObjects要做的就是將不在回收范圍內(nèi)的對(duì)象的標(biāo)志位從Live Bitmap拷貝到Mark Bitmap去。詳細(xì)做法就是分別遍歷Active堆和Zygote堆,假設(shè)它們處于不回范圍中,那么就對(duì)里面的對(duì)象在Live Bitmap中對(duì)應(yīng)的內(nèi)存塊拷貝到Mark Bitmap的對(duì)應(yīng)位置去。 計(jì)算一個(gè)對(duì)
54、象在一個(gè)Bitmap的標(biāo)志位所在的內(nèi)存塊是經(jīng)過宏HB_OFFSET_TO_INDEX和HB_OFFSET_TO_BYTE_INDEX來實(shí)現(xiàn)的。這兩個(gè)宏的詳細(xì)實(shí)現(xiàn)可以參考前面一文。 回到函數(shù)dvmHeapMarkRootSet中,我們繼續(xù)分析函數(shù)dvmVisitRoots的實(shí)現(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); 這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Visit.cpp。 參數(shù)visitor指向的是函數(shù)rootMarkObjectVisitor,它擔(dān)任將根集對(duì)象在Mark Bitmap中的位設(shè)置為1。我們后面再分析
58、這個(gè)函數(shù)的實(shí)現(xiàn)。 以下對(duì)象將被視為根集對(duì)象: 1. 被加載到Dalvik虛擬機(jī)的類對(duì)象。這些類對(duì)象緩存在gDvm.loadedClasses指向的一個(gè)Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 2. 在Dalvik虛擬機(jī)內(nèi)部創(chuàng)建的原子類,例如Double、Boolean類等,經(jīng)過函數(shù)visitPrimitiveTypes來遍歷和標(biāo)志。 3. 注冊(cè)在調(diào)試器的對(duì)象。這些對(duì)象保管在gDvm.dbgRegistry指向的一個(gè)Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 4. 字符串常量池中的String對(duì)象。這些對(duì)象保管保管在gDvm.literalStrin
59、gs指向的一個(gè)Hash表中,經(jīng)過函數(shù)visitHashTable來遍歷和標(biāo)志。 5. 在JNI中創(chuàng)建的全局援用對(duì)象所援用的對(duì)象。這些被援用對(duì)象保管在gDvm.jniGlobalRefTable指向的一個(gè)間接援用表中,經(jīng)過函數(shù)visitIndirectRefTable來遍歷和標(biāo)志。 6. 在JNI中經(jīng)過GetStringUTFChars、GetByteArrayElements等接口訪問字符串或者數(shù)組時(shí)被Pin住的對(duì)象。這些對(duì)象保管在gDvm.jniPinRefTable指向的一個(gè)援用表中,經(jīng)過函數(shù)visitReferenceTable來遍歷和標(biāo)志。 7. 當(dāng)前位于Davlik虛擬機(jī)線程的調(diào)用棧
60、的對(duì)象。這些對(duì)象記錄在棧幀中,經(jīng)過函數(shù)visitThreads來遍歷和標(biāo)志。 8. 在Dalvik虛擬機(jī)內(nèi)部創(chuàng)建的OutOfMemory異常對(duì)象,這個(gè)對(duì)象保管在gDvm.outOfMemoryObj中。 9. 在Dalvik虛擬機(jī)內(nèi)部創(chuàng)建的InternalError異常對(duì)象,這個(gè)對(duì)象保管在gDernalErrorObj中。 10. 在Dalvik虛擬機(jī)內(nèi)部創(chuàng)建的N oClassDefFoundError異常對(duì)象,這個(gè)對(duì)象保管在gDvm.noClassDefFoundErrorObj中。 在上述這些根集對(duì)象中,最復(fù)雜和最難遍歷和標(biāo)志的就是位于Dalvik虛擬機(jī)線程棧中的對(duì)象,因此接下
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 食品安全追溯消費(fèi)者信任反饋建立
- 專業(yè)基礎(chǔ)-房地產(chǎn)經(jīng)紀(jì)人《專業(yè)基礎(chǔ)》真題匯編3
- 農(nóng)場(chǎng)半年度工作匯報(bào)
- 統(tǒng)編版五年級(jí)語文上冊(cè)寒假作業(yè)(十三)有答案
- 二零二五版共有產(chǎn)權(quán)房轉(zhuǎn)讓協(xié)議書3篇
- 二零二五年智能大棚土地承包合作協(xié)議范本3篇
- 宿州航空職業(yè)學(xué)院《英語專業(yè)前沿課程》2023-2024學(xué)年第一學(xué)期期末試卷
- 二零二五版公共安全防范承包合同3篇
- 二零二五年食品包裝設(shè)計(jì)及委托加工合同
- 蘇教版初一英語試卷單選題100道及答案
- 春季餐飲營(yíng)銷策劃
- 企業(yè)會(huì)計(jì)機(jī)構(gòu)的職責(zé)(2篇)
- 《疥瘡的防治及治療》課件
- Unit4 What can you do Part B read and write (說課稿)-2024-2025學(xué)年人教PEP版英語五年級(jí)上冊(cè)
- 2025年MEMS傳感器行業(yè)深度分析報(bào)告
- 《線控底盤技術(shù)》2024年課程標(biāo)準(zhǔn)(含課程思政設(shè)計(jì))
- 學(xué)校對(duì)口幫扶計(jì)劃
- 倉(cāng)庫(kù)倉(cāng)儲(chǔ)安全管理培訓(xùn)課件模板
- 風(fēng)力發(fā)電場(chǎng)運(yùn)行維護(hù)手冊(cè)
- 河道旅游開發(fā)合同
- 情人合同范例
評(píng)論
0/150
提交評(píng)論