




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Dalvik虛擬機(jī)垃圾收集〔GC〕過(guò)程分析前面我們分析了Dalvik虛擬機(jī)堆的創(chuàng)立過(guò)程,以及Java對(duì)象在堆上的分配過(guò)程。這些知識(shí)都是理解Dalvik虛擬機(jī)垃圾收集過(guò)程的根底。垃圾收集是一個(gè)復(fù)雜的過(guò)程,它要將那些不再被引用的對(duì)象進(jìn)行回收。一方面要求Dalvik虛擬機(jī)能夠標(biāo)記出哪些對(duì)象是不再被引用的。另一方面要求Dalvik虛擬機(jī)盡快地回收內(nèi)存,防止應(yīng)用程序長(zhǎng)時(shí)間停頓。本文就將詳細(xì)分析Dalvik虛擬機(jī)是如何解決上述問(wèn)題完成垃圾收集過(guò)程的。Dalvik虛擬機(jī)使用Mark-Sweep算法來(lái)進(jìn)行垃圾收集。顧名思義,Mark-Sweep算法就是為Mark和Sweep兩個(gè)階段進(jìn)行垃圾回收。其中,Mark階段從根集〔RootSet〕開(kāi)始,遞歸地標(biāo)記出當(dāng)前所有被引用的對(duì)象,而Sweep階段負(fù)責(zé)回收那些沒(méi)有被引用的對(duì)象。在分析Dalvik虛擬機(jī)使用的Mark-Sweep算法之前,我們先來(lái)了解一下什么情況下會(huì)觸發(fā)GC。Dalvik虛擬機(jī)在三種情況下會(huì)觸發(fā)四種類型的GC。每一種類型GC使用一個(gè)GcSpec結(jié)構(gòu)體來(lái)描述,它的定義如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片structGcSpec{/*Iftrue,onlytheapplicationheapisthreatened.*/boolisPartial;/*Iftrue,thetraceisrunconcurrentlywiththemutator.*/boolisConcurrent;/*Togglesforthesoftreferenceclearingpolicy.*/booldoPreserve;/*Anameforthisgarbagecollectionmode.*/constchar*reason;};這個(gè)結(jié)構(gòu)體定義在文件dalvik/vm/alloc/Heap.h中。GcSpec結(jié)構(gòu)體的各個(gè)成員變量的含義如下所示:isPartial:為true時(shí),表示僅僅回收Active堆的垃圾;為false時(shí),表示同時(shí)回收Active堆和Zygote堆的垃圾。isConcurrent:為true時(shí),表示執(zhí)行并行GC;為false時(shí),表示執(zhí)行非并行GC。doPreserve:為true時(shí),表示在執(zhí)行GC的過(guò)程中,不回收軟引用引用的對(duì)象;為false時(shí),表示在執(zhí)行GC的過(guò)程中,回收軟引用引用的對(duì)象。reason:一個(gè)描述性的字符串。Davlik虛擬機(jī)定義了四種類的GC,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片/*Notenoughspaceforan"ordinary"Objecttobeallocated.*/externconstGcSpec*GC_FOR_MALLOC;/*AutomaticGCtriggeredbyexceedingaheapoccupancythreshold.*/externconstGcSpec*GC_CONCURRENT;/*ExplicitGCviaRuntime.gc(),VMRuntime.gc(),orSIGUSR1.*/externconstGcSpec*GC_EXPLICIT;/*FinalattempttoreclaimmemorybeforethrowinganOOM.*/externconstGcSpec*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īng)用程序調(diào)用System.gc、VMRuntime.gc接口或者收到SIGUSR1信號(hào)時(shí)觸發(fā)的GC。GC_BEFORE_OOM:表示是在準(zhǔn)備拋OOM異常之前進(jìn)行的最后努力而觸發(fā)的GC。實(shí)際上,GC_FOR_MALLOC、GC_CONCURRENT和GC_BEFORE_OOM三種類型的GC都是在分配對(duì)象的過(guò)程觸發(fā)的。在前面一文,我們提到,Dalvik虛擬機(jī)在Java堆上分配對(duì)象的時(shí)候,在碰到分配失敗的情況,會(huì)嘗試調(diào)用函數(shù)gcForMalloc進(jìn)行垃圾回收。函數(shù)gcForMalloc的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidgcForMalloc(boolclearSoftReferences){constGcSpec*spec=clearSoftReferences?GC_BEFORE_OOM:GC_FOR_MALLOC;dvmCollectGarbageInternal(spec);}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。參數(shù)clearSOftRefereces表示是否要對(duì)軟引用引用的對(duì)象進(jìn)行回收。如果要對(duì)軟引用引用的對(duì)象進(jìn)行回收,那么就說(shuō)明當(dāng)前內(nèi)存是非常緊張的了,因此,這時(shí)候執(zhí)行的就是GC_BEFORE_OOM類型的GC。否那么的話,執(zhí)行的就是GC_FOR_MALLOC類型的GC。它們都是通過(guò)調(diào)用函數(shù)dvmCollectGarbageInternal來(lái)執(zhí)行的。在前面一文,我們也提到,當(dāng)Dalvik虛擬機(jī)成功地在堆上分配一個(gè)對(duì)象之后,會(huì)檢查一下當(dāng)前分配的內(nèi)存是否超出一個(gè)閥值,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片void*dvmHeapSourceAlloc(size_tn){HeapSource*hs=gHs;Heap*heap=hs2heap(hs);if(heap->bytesAllocated+n>hs->softLimit){returnNULL;}void*ptr;if(gDvm.lowMemoryMode){ptr=mspace_malloc(heap->msp,n);}else{ptr=mspace_calloc(heap->msp,1,n);}countAllocation(heap,ptr);if(heap->bytesAllocated>heap->concurrentStartBytes){dvmSignalCond(&gHs->gcThreadCond);}returnptr;}這個(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〕。如果大于,那么就會(huì)通過(guò)條件變量gHs->gcThreadCond喚醒GC線程進(jìn)行垃圾回收。預(yù)設(shè)的閥值〔heap->concurrentStartBytes〕是一個(gè)比指定的堆最小空閑內(nèi)存小128K的數(shù)值。也就是說(shuō),當(dāng)堆的空閑內(nèi)缺乏時(shí),就會(huì)觸發(fā)GC_CONCURRENT類型的GC。GC線程是Dalvik虛擬機(jī)啟動(dòng)的過(guò)程中創(chuàng)立的,它的執(zhí)行體函數(shù)是gcDaemonThread,實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoid*gcDaemonThread(void*arg){dvmChangeStatus(NULL,THREAD_VMWAIT);dvmLockMutex(&gHs->gcThreadMutex);while(gHs->gcThreadShutdown!=true){booltrim=false;if(gHs->gcThreadTrimNeeded){intresult=dvmRelativeCondWait(&gHs->gcThreadCond,&gHs->gcThreadMutex,HEAP_TRIM_IDLE_TIME_MS,0);if(result==ETIMEDOUT){/*TimedoutwaitingforaGCrequest,scheduleaheaptrim.*/trim=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(NULL,THREAD_VMWAIT);}dvmUnlockHeap();}dvmChangeStatus(NULL,THREAD_RUNNING);returnNULL;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。GC線程平時(shí)沒(méi)事的時(shí)候,就在條件變量gHs->gcThreadCond上進(jìn)行等待HEAP_TRIM_IDLE_TIME_MS毫秒〔5000毫秒〕。如果在HEAP_TRIM_IDLE_TIME_MS毫秒內(nèi),都沒(méi)有得到執(zhí)行GC的通知,那么它就調(diào)用函數(shù)trimHeaps對(duì)Java堆進(jìn)行裁剪,以便可以將堆上的一些沒(méi)有使用到的內(nèi)存交還給內(nèi)核。函數(shù)trimHeaps的實(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來(lái)鎖定堆的。等到GC執(zhí)行完畢,再調(diào)用函數(shù)dvmUnlockHeap來(lái)解除對(duì)堆的鎖定。這與函數(shù)gcForMalloc調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)行類型為GC_FOR_MALLOC和GC_CONCURRENT的GC是一樣的。只不過(guò),對(duì)堆進(jìn)行鎖定和解鎖的操作是在調(diào)用堆棧上的函數(shù)dvmMalloc進(jìn)行的,具體可以參考前面一文。當(dāng)應(yīng)用程序調(diào)用System.gc、VMRuntime.gc接口,或者接收到SIGUSR1信號(hào)時(shí),最終會(huì)調(diào)用到函數(shù)dvmCollectGarbage,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voiddvmCollectGarbage(){if(gDvm.disableExplicitGc){return;}dvmLockHeap();dvmWaitForConcurrentGcToComplete();dvmCollectGarbageInternal(GC_EXPLICIT);dvmUnlockHeap();}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。如果Davik虛擬機(jī)在啟動(dòng)的時(shí)候,通過(guò)-XX:+DisableExplicitGC選項(xiàng)禁用了顯式GC,那么函數(shù)dvmCollectGarbage什么也不做就返回了。這意味著Dalvik虛擬機(jī)可能會(huì)不支持應(yīng)用程序顯式的GC請(qǐng)求。一旦Dalvik虛擬機(jī)支持顯式GC,那么函數(shù)dvmCollectGarbage就會(huì)先鎖定堆,并且等待有可能正在執(zhí)行的GC_CONCURRENT類型的GC完成之后,再調(diào)用函數(shù)dvmCollectGarbageInternal進(jìn)行類型為GC_EXPLICIT的GC。以上就是三種情況下會(huì)觸發(fā)四種類型的GC。有了這個(gè)背景知識(shí)之后,我們接下來(lái)就開(kāi)始分析Dalvik虛擬機(jī)使用的Mark-Sweep算法,整個(gè)過(guò)程如圖1所示:圖1Dalvik虛擬機(jī)垃圾收集過(guò)程Dalvik虛擬機(jī)支持非并行和并行兩種GC。在圖1中,左邊是非并行GC的執(zhí)行過(guò)程,而右邊是并行GC的執(zhí)行過(guò)程。它們的總體流程是相似的,主要差異在于前者在執(zhí)行的過(guò)程中一直是掛起非GC線程的,而后者是有條件地掛起非GC線程。由前面的分析可以知道,無(wú)論是并行GC,還是非并行GC,它們都是通過(guò)函數(shù)dvmCollectGarbageInternal來(lái)執(zhí)行的。函數(shù)dvmCollectGarbageInternal的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voiddvmCollectGarbageInternal(constGcSpec*spec){if(gcHeap->gcRunning){return;}gcHeap->gcRunning=true;dvmSuspendAllThreads(SUSPEND_FOR_GC);if(!dvmHeapBeginMarkStep(spec->isPartial)){dvmAbort();}dvmHeapMarkRootSet();if(spec->isConcurrent){dvmClearCardTable();dvmUnlockHeap();dvmResumeAllThreads(SUSPEND_FOR_GC);}dvmHeapScanMarkedObjects();if(spec->isConcurrent){dvmLockHeap();dvmSuspendAllThreads(SUSPEND_FOR_GC);dvmHeapReMarkRootSet();dvmHeapReScanMarkedObjects();}dvmHeapProcessReferences(&gcHeap->softReferences,spec->doPreserve==false,&gcHeap->weakReferences,&gcHeap->finalizerReferences,&gcHeap->phantomReferences);dvmHeapSweepSystemWeaks();dvmHeapSourceSwapBitmaps();if(spec->isConcurrent){dvmUnlockHeap();dvmResumeAllThreads(SUSPEND_FOR_GC);}dvmHeapSweepUnmarkedObjects(spec->isPartial,spec->isConcurrent,&numObjectsFreed,&numBytesFreed);dvmHeapFinishMarkStep();if(spec->isConcurrent){dvmLockHeap();}dvmHeapSourceGrowForUtilization();gcHeap->gcRunning=false;if(spec->isConcurrent){dvmBroadcastCond(&gDvm.gcHeapCond);}if(!spec->isConcurrent){dvmResumeAllThreads(SUSPEND_FOR_GC);}dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。前面提到,在調(diào)用函數(shù)dvmCollectGarbageInternal之前,堆是已經(jīng)被鎖定了的,因此這時(shí)候任何需要堆上分配對(duì)象的線程都會(huì)被掛起。注意,這不會(huì)影響到那些不需要在堆上分配對(duì)象的線程。在圖1中顯示的GC流程中,除了第一步的LockHeap和最后一步的UnlockHeap,都對(duì)應(yīng)于函數(shù)dvmCollectGarbageInternal的實(shí)現(xiàn),它的執(zhí)行邏輯如下所示:第1步到第3步用于并行和非并行GC:1.調(diào)用函數(shù)dvmSuspendAllThreads掛起所有的線程,以免它們干擾GC。2.調(diào)用函數(shù)dvmHeapBeginMarkStep初始化MarkStack,并且設(shè)定好GC范圍。關(guān)于MarkStack的介紹,可以參考前面一文。3.調(diào)用函數(shù)dvmHeapMarkRootSet標(biāo)記根集對(duì)象。第4到第6步用于并行GC:4.調(diào)用函數(shù)dvmClearCardTable清理CardTable。關(guān)于CardTable的介紹,可以參考前面一文。因?yàn)榻酉聛?lái)我們將會(huì)喚醒第1步掛起的線程。并且使用這個(gè)CardTable來(lái)記錄那些在GC過(guò)程中被修改的對(duì)象。5.調(diào)用函數(shù)dvmUnlock解鎖堆。這個(gè)是針對(duì)調(diào)用函數(shù)dvmCollectGarbageInternal執(zhí)行GC前的堆鎖定操作。6.調(diào)用函數(shù)dvmResumeAllThreads喚醒第1步掛起的線程。第7步用于并行和非并行GC:7.調(diào)用函數(shù)dvmHeapScanMarkedObjects從第3步獲得的根集對(duì)象開(kāi)始,歸遞標(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ì)象。因?yàn)橛锌赡茉诘?步到第6步的執(zhí)行過(guò)程中,有線程創(chuàng)立了新的根集對(duì)象。11.調(diào)用函數(shù)dvmHeapReScanMarkedObjects歸遞標(biāo)記那些在第4步到第6步的執(zhí)行過(guò)程中被修改的對(duì)象。這些對(duì)象記錄在CardTable中。第12步到第14步用于并行和非并行GC:12.調(diào)用函數(shù)dvmHeapProcessReferences處理那些被軟引用〔SoftReference〕、弱引用〔WeakReference〕和影子引用〔PhantomReference〕引用的對(duì)象,以及重寫(xiě)了finalize方法的對(duì)象。這些對(duì)象都是需要特殊處理的。13.調(diào)用函數(shù)dvmHeapSweepSystemWeaks回收系統(tǒng)內(nèi)部使用的那些被弱引用引用的對(duì)象。14.調(diào)用函數(shù)dvmHeapSourceSwapBitmaps交換LiveBitmap和MarkBitmap。執(zhí)行了前面的13步之后,所有還被引用的對(duì)象在MarkBitmap中的bit都被設(shè)置為1。而LiveBitmap記錄的是當(dāng)前GC前還被引用著的對(duì)象。通過(guò)交換這兩個(gè)Bitmap,就可以使得當(dāng)前GC完成之后,使得LiveBitmap記錄的是下次GC前還被引用著的對(duì)象。第15步和第16步用于并行GC:15.調(diào)用函數(shù)dvmUnlock解鎖堆。這個(gè)是針對(duì)前面第8步的操作。16.調(diào)用函數(shù)dvmResumeAllThreads喚醒第9步掛起的線程。第17步和第18步用于并行和非并行GC:17.調(diào)用函數(shù)dvmHeapSweepUnmarkedObjects回收那些沒(méi)有被引用的對(duì)象。沒(méi)有被引用的對(duì)象就是那些在執(zhí)行第14步之前,在LiveBitmap中的bit設(shè)置為1,但是在MarkBitmap中的bit設(shè)置為0的對(duì)象。18.調(diào)用函數(shù)dvmHeapFinishMarkStep重置MarkBitmap以及MarkStack。這個(gè)是針對(duì)前面第2步的操作。第19步用于并行GC:19.調(diào)用函數(shù)dvmLockHeap重新鎖定堆。這個(gè)是針對(duì)前面第15步的操作。第20步用于并行和非并行GC:20.調(diào)用函數(shù)dvmHeapSourceGrowForUtilization根據(jù)設(shè)置的堆目標(biāo)利用率調(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將那些目標(biāo)對(duì)象已經(jīng)被回收了的引用對(duì)象增加到相應(yīng)的Java隊(duì)列中去,以便應(yīng)用程序可以知道哪些引用引用的對(duì)象已經(jīng)被回收了。以上就是并行和非并行GC的執(zhí)行總體流程,它們的主要區(qū)別在于,前者在GC過(guò)程中,有條件地掛起和喚醒非GC線程,而后者在執(zhí)行GC的過(guò)程中,一直都是掛起非GC線程的。并行GC通過(guò)有條件地掛起和喚醒非GC線程,就可以使得應(yīng)用程序獲得更好的響應(yīng)性。但是我們也應(yīng)該看到,并行GC需要多執(zhí)行一次標(biāo)記根集對(duì)象以及遞歸標(biāo)記那些在GC過(guò)程被訪問(wèn)了的對(duì)象的操作。也就是說(shuō),并行GC在使用得應(yīng)用程序獲得更好的響應(yīng)性的同時(shí),也需要花費(fèi)更多的CPU資源。為了更深入地理解GC的執(zhí)行過(guò)程,接下來(lái)我們?cè)僭敿?xì)分析在上述的23步中涉及到的重要函數(shù)。1.dvmSuspendAllThreads函數(shù)dvmSuspendAllThreads用來(lái)掛起Dalvik虛擬機(jī)中除當(dāng)前線程之外的其它線程,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voiddvmSuspendAllThreads(SuspendCausewhy){Thread*self=dvmThreadSelf();Thread*thread;lockThreadSuspend("susp-all",why);dvmLockThreadList(self);lockThreadSuspendCount();for(thread=gDvm.threadList;thread!=NULL;thread=thread->next){if(thread==self)continue;/*debuggereventsdon'tsuspendJDWPthread*/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);}unlockThreadSuspendCount();for(thread=gDvm.threadList;thread!=NULL;thread=thread->next){if(thread==self)continue;/*debuggereventsdon'tsuspendJDWPthread*/if((why==SUSPEND_FOR_DEBUG||why==SUSPEND_FOR_DEBUG_EVENT)&&thread->handle==dvmJdwpGetDebugThread(gDvm.jdwpState))continue;/*waitfortheotherthreadtoseethependingsuspend*/waitForThreadSuspend(self,thread);}dvmUnlockThreadList();unlockThreadSuspend();}這個(gè)函數(shù)定義在文件dalvik/vm/Thread.cpp中。在以下三種情況下,當(dāng)前線程需要掛起其它線程:1.執(zhí)行GC。2.調(diào)試器請(qǐng)求。3.發(fā)生調(diào)試事件,如斷點(diǎn)命中。而且,上述三種情況有可能是同時(shí)發(fā)生的。例如,一個(gè)線程在分配對(duì)象的過(guò)程中發(fā)生GC的同時(shí),調(diào)試器也剛好請(qǐng)求掛起所有線程。這時(shí)候就需要保證每一個(gè)掛起操作都是順序執(zhí)行的,即要對(duì)掛起線程的操作進(jìn)行加鎖。這是通過(guò)調(diào)用函數(shù)函數(shù)lockThreadSuspend來(lái)實(shí)現(xiàn)的。函數(shù)lockThreadSuspend嘗試獲取gDvm._threadSuspendLock鎖。如果獲取失敗,就說(shuō)明有其它線程也正在請(qǐng)求掛起Dalvik虛擬機(jī)中的線程,包括當(dāng)前線程。每一個(gè)Dalvik虛擬機(jī)線程都有一個(gè)SuspendCount計(jì)數(shù),每當(dāng)它們都掛起的時(shí)候,對(duì)應(yīng)的SuspendCount計(jì)數(shù)就會(huì)加1,而當(dāng)被喚醒時(shí),對(duì)應(yīng)的SuspendCount計(jì)數(shù)就會(huì)減1。在獲取gDvm._threadSuspendLock鎖失敗的情況下,當(dāng)前線程按照一定的時(shí)間間隔檢查自己的SuspendCount,直到自己的SuspendCount等于0,并且能成功獲取gDvm._threadSuspendLock鎖為止。這樣就可以保證每一個(gè)掛起Dalvik虛擬機(jī)線程的請(qǐng)求都能夠得到順序執(zhí)行。從函數(shù)lockThreadSuspend返回之后,就說(shuō)明當(dāng)前線程可以執(zhí)行掛起其它線程的操作了。它首先要做的第一件事情是遍歷Dalvik虛擬機(jī)線程列表,并且調(diào)用函數(shù)dvmAddToSuspendCounts將列表里面的每一個(gè)線程對(duì)應(yīng)的SuspendCount都增加1,但是除了當(dāng)前線程之外。注意,當(dāng)掛起線程的請(qǐng)求是調(diào)試器發(fā)出或者是由調(diào)試事件觸發(fā)的時(shí)候,Dalvik虛擬機(jī)中的JDWP線程是不可以掛起的,因?yàn)镴DWP線程掛起來(lái)之后,就沒(méi)法調(diào)試了。在這種情況下,也不能將JDWP線程對(duì)應(yīng)的SuspendCount都增加1。通過(guò)調(diào)用函數(shù)dvmJdwpGetDebugThread可以獲得JDWP線程句柄,用這個(gè)句柄和Dalvik虛擬機(jī)線程列表中的線程句柄相比,就可以知道當(dāng)前遍歷的線程是否是JDWP線程。同時(shí),通過(guò)參數(shù)why可以知道當(dāng)掛起線程的請(qǐng)求是否是由調(diào)試器發(fā)出的或者由調(diào)試事件觸發(fā)的,注意,在增加被掛起線程的SuspendCount計(jì)數(shù)之前,必須要獲取gDvm.threadSuspendCountLock鎖。這個(gè)鎖的獲取和釋放可以通過(guò)函數(shù)lockThreadSuspendCount和unlockThreadSuspendCount完成。將被掛起線程的SuspendCount計(jì)數(shù)都增加1之后,接下來(lái)就是等待被掛起線程自愿將自己掛起來(lái)了。這是通過(guò)函數(shù)waitForThreadSuspend來(lái)實(shí)現(xiàn)。當(dāng)一個(gè)線程自愿將自己掛起來(lái)的時(shí)候,會(huì)將自己的狀態(tài)設(shè)置為非運(yùn)行狀態(tài)〔THREAD_RUNNING〕,這樣函數(shù)waitForThreadSuspend通過(guò)不斷地檢查一個(gè)線程的狀態(tài)是否處于非運(yùn)行狀態(tài)就可以知道它是否已經(jīng)掛起來(lái)了。那么,一個(gè)線程在什么情況才會(huì)自愿將自己掛起來(lái)呢?一個(gè)線程在執(zhí)行的過(guò)程中,會(huì)在適宜的時(shí)候檢查自己的SuspendCount計(jì)數(shù)。一旦該計(jì)數(shù)值不等于0,那么它就知道有線程請(qǐng)求掛起自己,因此它就會(huì)很配合地將自己的狀態(tài)設(shè)置為非運(yùn)行的,并且將自己掛起來(lái)。例如,當(dāng)一個(gè)線程通過(guò)解釋器執(zhí)行代碼時(shí),就會(huì)周期性地檢查自己的SuspendCount是否等于0。這里說(shuō)的周期性,實(shí)際上就是碰到IF指令、GOTO指令、SWITCH指令、RETURN指令和THROW指令等時(shí)。2.dvmHeapBeginMarkStep函數(shù)dvmHeapBeginMarkStep用來(lái)初始化堆的標(biāo)記范圍和MarkStack,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片booldvmHeapBeginMarkStep(boolisPartial){GcMarkContext*ctx=&gDvm.gcHeap->markContext;if(!createMarkStack(&ctx->stack)){returnfalse;}ctx->finger=NULL;ctx->immuneLimit=(char*)dvmHeapSourceGetImmuneLimit(isPartial);returntrue;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。在標(biāo)記過(guò)程中用到的各種信息都保存一個(gè)GcMarkContext結(jié)構(gòu)體描述的GC標(biāo)記上下文ctx中。其中,ctx->stack描述的是MarkStack,ctx->finger描述的是一個(gè)標(biāo)記指紋,在遞歸標(biāo)記對(duì)象時(shí)會(huì)用到,ctx->immuneLimit用來(lái)限定堆的標(biāo)記范圍。函數(shù)dvmHeapBeginMarkStep調(diào)用另外一個(gè)函數(shù)createMarkStack來(lái)初始化MarkStack,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticboolcreateMarkStack(GcMarkStack*stack){assert(stack!=NULL);size_tlength=dvmHeapSourceGetIdealFootprint()*sizeof(Object*)/(sizeof(Object)+HEAP_SOURCE_CHUNK_OVERHEAD);madvise(stack->base,length,MADV_NORMAL);stack->top=stack->base;returntrue;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。函數(shù)createMarkStack根據(jù)最壞情況來(lái)設(shè)置MarkStack的長(zhǎng)度,也就是用當(dāng)前堆的大小除以對(duì)象占用的最小內(nèi)存得到的結(jié)果。當(dāng)前堆的大小可以通過(guò)函數(shù)dvmHeapSourceGetIdealFootprint來(lái)獲得。在堆上分配的對(duì)象,都是從Object類繼承下來(lái)的,因此,每一個(gè)對(duì)象占用的最小內(nèi)存是sizeof(Object)。由于在為對(duì)象分配內(nèi)存時(shí),還需要額外的內(nèi)存來(lái)保存管理信息,例如實(shí)際分配給對(duì)象的內(nèi)存字節(jié)數(shù)。這些額外的管理信息占用的內(nèi)存字節(jié)數(shù)通過(guò)宏HEAP_SOURCE_CHUNK_OVERHEAD來(lái)描述。由于MarkStack實(shí)際上是一個(gè)Object指針數(shù)組,因此有了上面的信息之后,我們就可以計(jì)算出MarkStack的長(zhǎng)度length。MarkStack使用的內(nèi)存塊在Dalvik虛擬機(jī)啟動(dòng)的時(shí)候就創(chuàng)立好的,具體參考前面一文,起始地址位于stack->base中。由于這塊內(nèi)存開(kāi)始的時(shí)候被函數(shù)madvice設(shè)置為MADV_DONTNEED,因此這里要將它重新設(shè)置為MADV_NORMAL,以便可以通知內(nèi)核,這塊內(nèi)存要開(kāi)始使用了。MarkStack的棧頂stack->top指向的是MarkStack內(nèi)存塊的起始位置,以后就會(huì)從這個(gè)位置開(kāi)始由小到大增長(zhǎng)。回到函數(shù)dvmHeapBeginMarkStep中,ctx->immuneLimit記錄的實(shí)際上是堆的起始標(biāo)記位置。在此位置之前的對(duì)象,都不會(huì)被GC。這個(gè)位置是通過(guò)函數(shù)dvmHeapSourceGetImmuneLimit來(lái)確定的,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片void*dvmHeapSourceGetImmuneLimit(boolisPartial){if(isPartial){returnhs2heap(gHs)->base;}else{returnNULL;}}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。當(dāng)參數(shù)isPartial等于true時(shí),函數(shù)dvmHeapSourceGetImmuneLimit返回的是Active堆的起始位置,否那么的話就返回NULL值。也就是說(shuō),如果當(dāng)前執(zhí)行的GC只要求回收局部垃圾,那么就只回收Active堆的垃圾,否那么的話,就同時(shí)回收Active堆和Zygote堆的垃圾。3.dvmHeapMarkRootSet函數(shù)dvmHeapMarkRootSet用來(lái)的標(biāo)記根集對(duì)象,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片/*Markthesetofrootobjects.**Thingsweneedtoscan:*-Systemclassesdefinedbyrootclassloader*-Foreachthread:*-Interpretedstack,fromtopto"curFrame"*-Dalvikregisters(args+localvars)*-JNIlocalreferences*-AutomaticVMlocalreferences(TrackedAlloc)*-AssociatedThread/VMThreadobject*-ThreadGroups(couldtrack&startwiththeseinsteadofworking*upwardfromThreads)*-Exceptioncurrentlybeingthrown,ifpresent*-JNIglobalreferences*-Internedstringtable*-Primitiveclasses*-Specialobjects*-gDvm.outOfMemoryObj*-Objectsindebuggerobjectregistry**Don'tneed:*-Nativestack(forin-progressstuffintheVM)*-TheTrackedAllocstuffwatchesallnativeVMreferences.*/voiddvmHeapMarkRootSet(){GcHeap*gcHeap=gDvm.gcHeap;dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);dvmVisitRoots(rootMarkObjectVisitor,&gcHeap->markContext);}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/MarkSweep.cpp中。通過(guò)注釋我們可以看到根集對(duì)象都包含了哪些對(duì)象。總的來(lái)說(shuō),就是包含兩大類對(duì)象。一類是Dalvik虛擬機(jī)內(nèi)部使用的全局對(duì)象,另一類是應(yīng)用程序正在使用的對(duì)象。前者會(huì)維護(hù)在內(nèi)部的一些數(shù)據(jù)結(jié)構(gòu)中,例如Hash表,后者維護(hù)在調(diào)用棧中。對(duì)這些根集對(duì)象進(jìn)行標(biāo)記都是通過(guò)函數(shù)dvmVisitRoots和回調(diào)函數(shù)rootMarkObjectVisitor進(jìn)行的。此外,我們還需要將不在堆回收范圍內(nèi)的對(duì)象也看作是根集對(duì)象,以便后面可以使用統(tǒng)一的方法來(lái)遍歷這兩類對(duì)象所引用的其它對(duì)象。標(biāo)記不在堆回收范圍內(nèi)的對(duì)象是通過(guò)函數(shù)dvmMarkImmuneObjects來(lái)實(shí)現(xiàn)的,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voiddvmMarkImmuneObjects(constchar*immuneLimit){for(size_ti=1;i<gHs->numHeaps;++i){if(gHs->heaps[i].base<immuneLimit){assert(gHs->heaps[i].limit<=immuneLimit);/*Computethenumberofwordstocopyinthebitmap.*/size_tindex=HB_OFFSET_TO_INDEX((uintptr_t)gHs->heaps[i].base-gHs->liveBits.base);/*Computethestartingoffsetintheliveandmarkbits.*/char*src=(char*)(gHs->liveBits.bits+index);char*dst=(char*)(gHs->markBits.bits+index);/*Computethenumberofbytesofthelivebitmaptocopy.*/size_tlength=HB_OFFSET_TO_BYTE_INDEX(gHs->heaps[i].limit-gHs->heaps[i].base);/*Dothecopy.*/memcpy(dst,src,length);/*Makesuremaxpointstotheaddressofthehighestsetbit.*/if(gHs->markBits.max<(uintptr_t)gHs->heaps[i].limit){gHs->markBits.max=(uintptr_t)gHs->heaps[i].limit;}}}}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。從前面分析的函數(shù)dvmHeapSourceGetImmuneLimit可以知道,參數(shù)immuneList要么是等于Zygote堆的最大地址值,要么是等于NULL。這取決于當(dāng)前GC要執(zhí)行的是全部垃圾回收還是局部垃圾回收。函數(shù)dvmMarkImmuneObjects是在當(dāng)前GC執(zhí)行之前調(diào)用的,這意味著當(dāng)前存活的對(duì)象都標(biāo)記在LiveBitmap中?,F(xiàn)在函數(shù)dvmMarkImmuneObjects要做的就是將不在回收范圍內(nèi)的對(duì)象的標(biāo)記位從LiveBitmap拷貝到MarkBitmap去。具體做法就是分別遍歷Active堆和Zygote堆,如果它們處于不回范圍中,那么就對(duì)里面的對(duì)象在LiveBitmap中對(duì)應(yīng)的內(nèi)存塊拷貝到MarkBitmap的對(duì)應(yīng)位置去。計(jì)算一個(gè)對(duì)象在一個(gè)Bitmap的標(biāo)記位所在的內(nèi)存塊是通過(guò)宏HB_OFFSET_TO_INDEX和HB_OFFSET_TO_BYTE_INDEX來(lái)實(shí)現(xiàn)的。這兩個(gè)宏的具體實(shí)現(xiàn)可以參考前面一文?;氐胶瘮?shù)dvmHeapMarkRootSet中,我們繼續(xù)分析函數(shù)dvmVisitRoots的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voiddvmVisitRoots(RootVisitor*visitor,void*arg){assert(visitor!=NULL);visitHashTable(visitor,gDvm.loadedClasses,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);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);(*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,它負(fù)責(zé)將根集對(duì)象在MarkBitmap中的位設(shè)置為1。我們后面再分析這個(gè)函數(shù)的實(shí)現(xiàn)。以下對(duì)象將被視為根集對(duì)象:1.被加載到Dalvik虛擬機(jī)的類對(duì)象。這些類對(duì)象緩存在gDvm.loadedClasses指向的一個(gè)Hash表中,通過(guò)函數(shù)visitHashTable來(lái)遍歷和標(biāo)記。2.在Dalvik虛擬機(jī)內(nèi)部創(chuàng)立的原子類,例如Double、Boolean類等,通過(guò)函數(shù)visitPrimitiveTypes來(lái)遍歷和標(biāo)記。3.注冊(cè)在調(diào)試器的對(duì)象。這些對(duì)象保存在gDvm.dbgRegistry指向的一個(gè)Hash表中,通過(guò)函數(shù)visitHashTable來(lái)遍歷和標(biāo)記。4.字符串常量池中的String對(duì)象。這些對(duì)象保存保存在gDvm.literalStrings指向的一個(gè)Hash表中,通過(guò)函數(shù)visitHashTable來(lái)遍歷和標(biāo)記。5.在JNI中創(chuàng)立的全局引用對(duì)象所引用的對(duì)象。這些被引用對(duì)象保存在gDvm.jniGlobalRefTable指向的一個(gè)間接引用表中,通過(guò)函數(shù)visitIndirectRefTable來(lái)遍歷和標(biāo)記。6.在JNI中通過(guò)GetStringUTFChars、GetByteArrayElements等接口訪問(wèn)字符串或者數(shù)組時(shí)被Pin住的對(duì)象。這些對(duì)象保存在gDvm.jniPinRefTable指向的一個(gè)引用表中,通過(guò)函數(shù)visitReferenceTable來(lái)遍歷和標(biāo)記。7.當(dāng)前位于Davlik虛擬機(jī)線程的調(diào)用棧的對(duì)象。這些對(duì)象記錄在棧幀中,通過(guò)函數(shù)visitThreads來(lái)遍歷和標(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)立的NoClassDefFoundError異常對(duì)象,這個(gè)對(duì)象保存在gDvm.noClassDefFoundErrorObj中。在上述這些根集對(duì)象中,最復(fù)雜和最難遍歷和標(biāo)記的就是位于Dalvik虛擬機(jī)線程棧中的對(duì)象,因此接下來(lái)我們就繼續(xù)分析函數(shù)發(fā)isitThreads的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidvisitThreads(RootVisitor*visitor,void*arg){Thread*thread;assert(visitor!=NULL);dvmLockThreadList(dvmThreadSelf());thread=gDvm.threadList;while(thread){visitThread(visitor,thread,arg);thread=thread->next;}dvmUnlockThreadList();}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Visit.cpp。所有的Dalvik虛擬機(jī)線程都保存在gDvm.threadList指向的一個(gè)列表中,函數(shù)visitThreads通過(guò)調(diào)用另外一個(gè)函數(shù)函數(shù)visitThread來(lái)遍歷和標(biāo)記每一個(gè)Dalvik虛擬機(jī)棧上對(duì)象。后者的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidvisitThread(RootVisitor*visitor,Thread*thread,void*arg){u4threadId;assert(visitor!=NULL);assert(thread!=NULL);threadId=thread->threadId;(*visitor)(&thread->threadObj,threadId,ROOT_THREAD_OBJECT,arg);(*visitor)(&thread->exception,threadId,ROOT_NATIVE_STACK,arg);visitReferenceTable(visitor,&thread->internalLocalRefTable,threadId,ROOT_NATIVE_STACK,arg);visitIndirectRefTable(visitor,&thread->jniLocalRefTable,threadId,ROOT_JNI_LOCAL,arg);if(thread->jniMonitorRefTable.table!=NULL){visitReferenceTable(visitor,&thread->jniMonitorRefTable,threadId,ROOT_JNI_MONITOR,arg);}visitThreadStack(visitor,thread,arg);}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Visit.cpp。對(duì)于每一個(gè)Dalvik虛擬機(jī)線程來(lái)說(shuō),除了它當(dāng)前棧上的對(duì)象需要標(biāo)記為根集對(duì)象之外,還有以下對(duì)象需要標(biāo)記為根集對(duì)象:1.用來(lái)描述Dalvik虛擬機(jī)線程的線程對(duì)象。這個(gè)對(duì)象保存在thread->threadObj中。2.當(dāng)前Davik虛擬機(jī)線程的未處理異常對(duì)象。這個(gè)對(duì)象保在在thread->expception中。3.Dalvik虛擬機(jī)線程內(nèi)部創(chuàng)立的對(duì)象,例如執(zhí)行過(guò)程中拋出的異常對(duì)象。這些對(duì)象保存在thread->internalLocalRefTable指向的一個(gè)引用表中。通過(guò)函數(shù)visitReferenceTable來(lái)遍歷和標(biāo)記。4.Dalvik虛擬機(jī)線程在JNI中創(chuàng)立的局部引用對(duì)象。這些對(duì)象保存在thread->jniLocalRefTable指向的一個(gè)間接引用表中,通過(guò)函數(shù)visitIndirectRefTable來(lái)遍歷和標(biāo)記。5.Dalvik虛擬機(jī)線程在JNI中創(chuàng)立的用于同步的Monitor對(duì)象。這些對(duì)象保存在thread->jniMonitorRefTable指向的一個(gè)引用表中,通過(guò)函數(shù)visitReferenceTable來(lái)遍歷和標(biāo)記。當(dāng)前棧上的對(duì)象通過(guò)函數(shù)visitThreadStack來(lái)遍歷和標(biāo)記,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidvisitThreadStack(RootVisitor*visitor,Thread*thread,void*arg){u4threadId=thread->threadId;constStackSaveArea*saveArea;for(u4*fp=(u4*)thread->interpSave.curFrame;fp!=NULL;fp=(u4*)saveArea->prevFrame){Method*method;saveArea=SAVEAREA_FROM_FP(fp);method=(Method*)saveArea->method;if(method!=NULL&&!dvmIsNativeMethod(method)){constRegisterMap*pMap=dvmGetExpandedRegisterMap(method);constu1*regVector=NULL;if(pMap!=NULL){intaddr=saveArea->xtra.currentPc-method->insns;regVector=dvmRegisterMapGetLine(pMap,addr);}if(regVector==NULL){for(size_ti=0;i<method->registersSize;++i){if(dvmIsValidObject((Object*)fp[i])){(*visitor)(&fp[i],threadId,ROOT_JAVA_FRAME,arg);}}}else{u2bits=1<<1;for(size_ti=0;i<method->registersSize;++i){bits>>=1;if(bits==1){bits=*regVector++|0x0100;}if((bits&0x1)!=0){(*visitor)(&fp[i],threadId,ROOT_JAVA_FRAME,arg);}}dvmReleaseRegisterMapLine(pMap,regVector);}}}}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Visit.cpp。在Dalvik虛擬機(jī)中,每一個(gè)Dalvik虛擬機(jī)線程都用一個(gè)Thread結(jié)構(gòu)體來(lái)描述。在Thread結(jié)構(gòu)體中,有一個(gè)成員變量interpSave,它指向的是一個(gè)InterpSaveState結(jié)構(gòu)體。在InterpSaveState結(jié)構(gòu)體中,又有一個(gè)成員變量curFrame,它指向線程的當(dāng)前調(diào)用棧幀,如圖2所示:圖2Dalvik虛擬機(jī)線程的調(diào)用棧在圖2中,Dalvik虛擬機(jī)調(diào)用棧由高地址往低地址增長(zhǎng),即棧頂位于低地址。在每一個(gè)調(diào)用幀前面,有一個(gè)類型為StackSaveArea的結(jié)構(gòu)體。在StackSaveArea結(jié)構(gòu)體中,有四個(gè)重要的成員變量prevFrame、savedPc、method和currentPc。其中,prevFrame指向前一個(gè)調(diào)用幀,savedPc指向當(dāng)前函數(shù)調(diào)用完成后的返回地址,method指向當(dāng)前調(diào)用的函數(shù),currentPc指向當(dāng)前執(zhí)行的指令。通過(guò)成員變量prevFrame,就可以從當(dāng)前調(diào)用棧幀開(kāi)始,遍歷整個(gè)調(diào)用棧。此外,通過(guò)成員變量method,可以知道一個(gè)調(diào)用棧幀對(duì)應(yīng)的調(diào)用函數(shù)是一個(gè)Java函數(shù)還是一個(gè)JNI函數(shù)。如果是一個(gè)JNI函數(shù),那么是不需要檢查它的棧幀的。因?yàn)镴NI函數(shù)不會(huì)在里面保存Java對(duì)象。那么在JNI函數(shù)里面創(chuàng)立或者訪問(wèn)的對(duì)象是如何管理的呢?我們?cè)贘NI函數(shù)中需要?jiǎng)?chuàng)立或者訪問(wèn)Java對(duì)象時(shí),都必須要通過(guò)JNI接口來(lái)進(jìn)行。JNI接口會(huì)把JNI函數(shù)創(chuàng)立或者訪問(wèn)的Java對(duì)象保存線程相關(guān)的一個(gè)JNILocalReferenceTable中。這些被JNILocalReferenceTable引用的Java對(duì)象,在JNI函數(shù)調(diào)用結(jié)束的時(shí)候,會(huì)相應(yīng)地被自動(dòng)釋放引用。但是有些情況下,我們需要在JNI函數(shù)中手動(dòng)釋放被JNILocalReferenceTable引用的Java對(duì)象,因?yàn)镴NILocalReferenceTable的大小是有限的。一旦一個(gè)JNI函數(shù)引用的Java對(duì)象數(shù)大于JNILocalReferenceTable的容量,那么就會(huì)發(fā)生錯(cuò)誤。這種情況特別容易發(fā)生循環(huán)語(yǔ)句中。由于JNI函數(shù)里面創(chuàng)立或者訪問(wèn)的對(duì)象保存在JNILocalReferenceTable中,因此,在前面分析的函數(shù)visitThread中,需要對(duì)每一個(gè)Dalvik虛擬機(jī)線程的JNILocalReferenceTable中的Java對(duì)象進(jìn)行遍歷和標(biāo)記,防止它們?cè)贕C過(guò)程中被回收。好了,我們現(xiàn)在將目光返回到Dalvik虛擬機(jī)線程的調(diào)用棧中,我們需要做的是遍歷那些非JNI函數(shù)調(diào)用,并且對(duì)位于棧幀里面的Java對(duì)象進(jìn)行標(biāo)記,以免它們?cè)贕C過(guò)程中被回收。我們知道,Dalvik虛擬機(jī)指令是基于存放器的。也就是說(shuō),無(wú)論函數(shù)里面的參數(shù),還是局部變量,都是保存在存放器中。但是,我們需要注意的是。這些存放器不是真實(shí)的硬件存放器,而是虛擬出來(lái)的。那么是怎么虛擬出來(lái)的呢?其實(shí)就是將存放器映射到調(diào)用棧幀對(duì)就的內(nèi)存塊上。因此,我們只需要遍歷調(diào)用棧幀對(duì)應(yīng)的內(nèi)存塊,就可以知道線程的調(diào)用棧都引用了哪些Java對(duì)象,從而可以對(duì)它們進(jìn)行標(biāo)記。現(xiàn)在,新的問(wèn)題又出現(xiàn)了。調(diào)用棧幀的數(shù)據(jù)不一定都是Java對(duì)象引用,還有可能是一個(gè)原子類型,例如整數(shù)、布爾變量或者浮點(diǎn)數(shù)。在遍歷調(diào)用棧幀的時(shí)候,我們是怎么知道里面的一個(gè)數(shù)據(jù)到底是Java對(duì)象引用還是一個(gè)原子類型呢?我們可以想到的一個(gè)方法是判斷它們值。如果它們的值是位于Java堆的范圍內(nèi),那么就認(rèn)為它們是Java對(duì)象引用。但是這樣做是不準(zhǔn)確的,因?yàn)橛锌赡苓@是一個(gè)整數(shù)值,并且這個(gè)整數(shù)值剛好落在Java堆的地址范圍之內(nèi)。不過(guò),我們寧愿將一個(gè)整數(shù)值誤認(rèn)為一個(gè)Java對(duì)象引用,也比漏掉一個(gè)Java對(duì)象引用好。因?yàn)閷⒁粋€(gè)整數(shù)值誤認(rèn)為一個(gè)Java對(duì)象引用,導(dǎo)致的后果僅僅是垃圾不能及時(shí)回收,而漏掉一個(gè)Java對(duì)象引用,那么意味著一個(gè)正在使用Java對(duì)象還在被引用的時(shí)候被回收。
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度倉(cāng)儲(chǔ)物流勞務(wù)派遣安全責(zé)任書(shū)
- 2025年度在線貸款中介居間合作協(xié)議
- 二零二五年度企業(yè)內(nèi)部員工外出安全免責(zé)合同
- 2025年度個(gè)人租房合同協(xié)議書(shū)模板(含租賃房屋維修費(fèi)用承擔(dān))
- 2025年度應(yīng)屆大學(xué)生實(shí)習(xí)合同
- 國(guó)際發(fā)展合作的中國(guó)實(shí)踐 第六期綠色發(fā)展援助篇
- 2025年度抖音網(wǎng)紅達(dá)人合作推廣合同模板
- 2025年度合作社土地入股與農(nóng)業(yè)資源環(huán)境監(jiān)測(cè)合作協(xié)議
- 2025年度房屋租賃合同租賃雙方租賃期間租賃物租賃權(quán)轉(zhuǎn)讓協(xié)議
- 沙石運(yùn)輸行業(yè)自律公約
- 低血糖健康宣教
- 《煉油化工基本知識(shí)》課件
- 關(guān)于高中語(yǔ)文教學(xué)中“微課”的運(yùn)用分析獲獎(jiǎng)科研報(bào)告論文
- 《射頻同軸電纜》課件2
- 口腔頜面部感染患者的營(yíng)養(yǎng)狀況及輔助營(yíng)養(yǎng)治療策略
- 以工代賑政策培訓(xùn)課件
- 垃圾分類校本教材
- 中職學(xué)生開(kāi)學(xué)心理知識(shí)講座
- 虛擬現(xiàn)實(shí)技術(shù)中的智能感知與識(shí)別技術(shù)應(yīng)用
- DD 2014-11 地面沉降干涉雷達(dá)數(shù)據(jù)處理技術(shù)規(guī)程
- 咖啡與茶文化培訓(xùn)1
評(píng)論
0/150
提交評(píng)論