Dalvik虚拟机垃圾收集(GC)过程分析_第1页
Dalvik虚拟机垃圾收集(GC)过程分析_第2页
Dalvik虚拟机垃圾收集(GC)过程分析_第3页
Dalvik虚拟机垃圾收集(GC)过程分析_第4页
Dalvik虚拟机垃圾收集(GC)过程分析_第5页
已阅读5页,还剩115页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Dalvik虚拟机垃圾收集(GC)过程分析前面我们分析了Dalvik虚拟机堆的创建过程,以及Java对象在堆上的分配过程。这些知识都是理解Dalvik虚拟机垃圾收集过程的基础。垃圾收集是一个复杂的过程,它要将那些不再被引用的对象进行回收。一方面要求Dalvik虚拟机能够标记出哪些对象是不再被引用的。另一方面要求Dalvik虚拟机尽快地回收内存,避免应用程序长时间停顿。本文就将详细分析Dalvik虚拟机是如何解决上述问题完成垃圾收集过程的。Dalvik虚拟机使用Mark-Sweep算法来进行垃圾收集。顾名思义,Mark-Sweep算法就是为Mark和Sweep两个阶段进行垃圾回收。其中,Mark

2、阶段从根集(Root Set)开始,递归地标记出当前所有被引用的对象,而Sweep阶段负责回收那些没有被引用的对象。在分析Dalvik虚拟机使用的Mark-Sweep算法之前,我们先来了解一下什么情况下会触发GC。 Dallvik虚拟拟机在三种情情况下会触发发四种类型的的GC。每一一种类型GCC使用一个GGcSpecc结构体来描描述,它的定定义如下所示示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片struct GcSpeec /* Iff truee, onlly thee appllicatiion heeap iss threeateneed.

3、 */ bool isParrtial; /* Iff truee, thee tracce is run cconcurrrentlly witth thee mutaator. */ bool isConncurreent; /* Tooggless for the ssoft rrefereence ccleariing poolicy. */ bool doPreeservee; /* A name for tthis ggarbagge colllectiion moode. */ constt charr *reaason; ; 这个结构构体定义在文文件dalvvik/vmm/al

4、looc/Heaap.h中。 GcSppec结构体体的各个成员员变量的含义义如下所示: isPaartiall: 为trrue时,表表示仅仅回收收Activve堆的垃圾圾;为fallse时,表表示同时回收收Activve堆和Zyygote堆堆的垃圾。 isCooncurrrent: 为truee时,表示执执行并行GCC;为fallse时,表表示执行非并并行GC。 doPrreservve: 为ttrue时,表表示在执行GGC的过程中中,不回收软软引用引用的的对象;为ffalse时时,表示在执执行GC的过过程中,回收收软引用引用用的对象。 reasson: 一一个描述性的的字符串。 Davlli

5、k虚拟机机定义了四种种类的GC,如如下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片/* Not enouggh spaace foor an ordiinary Objeect too be aallocaated. */ extern constt GcSppec *GGC_FORR_MALLLOC; /* Autoomaticc GC ttriggeered bby excceedinng a hheap ooccupaancy tthreshhold. */ extern constt GcSppec *GGC_CONNCURREENT;

6、 /* Expllicit GC viia Runntime.gc(), VMRuuntimee.gc(), or SIGUSSR1. */ extern constt GcSppec *GGC_EXPPLICITT; /* Finaal atttempt to reeclaimm memoory beefore throwwing aan OOMM. */ extern constt GcSppec *GGC_BEFFORE_OOOM; 这四个全全局变量声明明在文件daalvik/vm/allloc/HHeap.hh中。 它们的含含义如下所示示: GC_FFOR_MAALLOC: 表示是在在

7、堆上分配对对象时内存不不足触发的GGC。 GC_CCONCURRRENT: 表示是在在已分配内存存达到一定量量之后触发的的GC。 GC_EEXPLICCIT: 表表示是应用程程序调用Syystem.gc、VMMRuntiime.gcc接口或者收收到SIGUUSR1信号号时触发的GGC。 GC_BBEFOREE_OOM: 表示是在在准备抛OOOM异常之前前进行的最后后努力而触发发的GC。 实际上,GGC_FORR_MALLLOC、GCC_CONCCURRENNT和GC_BEFORRE_OOMM三种类型的的GC都是在在分配对象的的过程触发的的。 在前面一一文,我们提提到,Dallvik虚拟拟机在J

8、avva堆上分配配对象的时候候,在碰到分分配失败的情情况,会尝试试调用函数ggcForMMallocc进行垃圾回回收。 函数gccForMaalloc的的实现如下所所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片static void gcForrMallooc(boool cleearSofftRefeerencees) connst GccSpec *specc = cllearSooftRefferencces ? GC_BEEFORE_OOM : GC_FFOR_MAALLOC; dvmmColleectGarrbageIInternnal

9、(sppec); 这个函数定定义在文件ddalvikk/vm/aalloc/Heap.cpp中。 参数cleearSOfftRefeerecess表示是否要要对软引用引引用的对象进进行回收。如如果要对软引引用引用的对对象进行回收收,那么就表表明当前内存存是非常紧张张的了,因此此,这时候执执行的就是GGC_BEFFORE_OOOM类型的的GC。否则则的话,执行行的就是GCC_FOR_MALLOOC类型的GGC。它们都都是通过调用用函数dvmmColleectGarrbageIInternnal来执行行的。 在前面一文文,我们也提提到,当Daalvik虚虚拟机成功地地在堆上分配配一个对象之之后,会

10、检查查一下当前分分配的内存是是否超出一个个阀值,如下下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片void* ddvmHeaapSourrceAllloc(siize_t n) HeaapSourrce *hhs = ggHs; Heaap* heeap = hs2heeap(hss); if (heapp-byttesAlllocateed + nn hss-sofftLimiit) retuurn NUULL; voiid* pttr; if (gDvmm.lowMMemoryyMode) ptr = msppace_mmallocc(he

11、app-mspp, n); eelse ptr = msppace_ccallocc(heapp-mspp, 1, n); couuntAlllocatiion(heeap, pptr); if (heapp-byttesAlllocateed hheap-concuurrenttStarttBytess) dvmSSignallCond(&gHs-gcThhreadCCond); retturn pptr; 这个函数数定义在文件件dalviik/vm/allocc/HeappSourcce.cppp中。 函数dvvmHeappSourcceAllooc成功地在在Activve堆上分配配到一个

12、对象象之后,就会会检查Acttive堆当当前已经分配配的内存(hheap-bytessAlloccated)是是否大于预设设的阀值(hheap-concuurrenttStarttBytess)。如果大大于,那么就就会通过条件件变量gHss-gcTThreaddCond唤唤醒GC线程程进行垃圾回回收。预设的的阀值(heeap-cconcurrrentSStartBBytes)是是一个比指定定的堆最小空空闲内存小1128K的数数值。也就是是说,当堆的的空闲内不足足时,就会触触发GC_CCONCURRRENT类类型的GC。 GC线程程是Dalvvik虚拟机机启动的过程程中创建的,它它的执行体函函数

13、是gcDDaemonnThreaad,实现如如下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片static void *gcDaaemonTThreadd(voidd* argg) dvmmChanggeStattus(NUULL, TTHREADD_VMWAAIT); dvmmLockMMutex(&gHs-gcThhreadMMutex); whiile (ggHs-ggcThreeadShuutdownn != ttrue) booll trimm = faalse; if (gHs-gcThrreadTrrimNeeeded) int r

14、resultt = dvvmRelaativeCCondWaait(&ggHs-ggcThreeadConnd, &ggHs-ggcThreeadMuttex, HEEAP_TRRIM_IDDLE_TIIME_MSS, 0); if (rresultt = EETIMEDDOUT) /* Timmed ouut waiiting for aa GC rrequesst, scchedulle a hheap ttrim. */ ttrim = truee; ellse dvmWaaitConnd(&gHHs-gccThreaadCondd, &gHHs-gccThreaadMuteex); d

15、vmLLockHeeap(); if (!gDvmm.gcHeeap-ggcRunnning) dvmChhangeSStatuss(NULLL, THRREAD_RRUNNINNG); if (ttrim) ttrimHeeaps(); ggHs-ggcThreeadTriimNeedded = falsee; elsse ddvmColllectGGarbaggeInteernal(GC_COONCURRRENT); ggHs-ggcThreeadTriimNeedded = true; dvmChhangeSStatuss(NULLL, THRREAD_VVMWAITT); dvmUUn

16、lockkHeap(); dvmmChanggeStattus(NUULL, TTHREADD_RUNNNING); retturn NNULL; 这个函数数定义在文件件dalviik/vm/allocc/HeappSourcce.cppp中。 GC线程程平时没事的的时候,就在在条件变量ggHs-ggcThreeadConnd上进行等等待HEAPP_TRIMM_IDLEE_TIMEE_MS毫秒秒(50000毫秒)。如如果在HEAAP_TRIIM_IDLLE_TIMME_MS毫毫秒内,都没没有得到执行行GC的通知知,那么它就就调用函数ttrimHeeaps对JJava堆进进行裁剪,以以便可以将堆

17、堆上的一些没没有使用到的的内存交还给给内核。函数数trimHHeaps的的实现可以参参考前面一文文。否则的话话,就会调用用函数dvmmColleectGarrbageIInternnal进行类类型为GC_CONCUURRENTT的GC。 注意,函函数gcDaaemonTThreadd在调用函数数dvmCoollecttGarbaageIntternall进行类型为为GC_COONCURRRENT的GGC之前,会会先调用函数数dvmLoockHeaap来锁定堆堆的。等到GGC执行完毕毕,再调用函函数dvmUUnlockkHeap来来解除对堆的的锁定。这与与函数gcFForMallloc调用用函数

18、dvmmColleectGarrbageIInternnal进行类类型为GC_FOR_MMALLOCC和GC_CCONCURRRENT的的GC是一样样的。只不过过,对堆进行行锁定和解锁锁的操作是在在调用堆栈上上的函数dvvmMallloc进行的的,具体可以以参考前面一一文。 当应用程程序调用Syystem.gc、VMMRuntiime.gcc接口,或者者接收到SIIGUSR11信号时,最最终会调用到到函数dvmmColleectGarrbage,它它的实现如下下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片void dvvmColllectGaa

19、rbagee() if (gDvmm.disaableExxpliciitGc) retuurn; dvmmLockHHeap(); dvmmWaitFForConncurreentGcTToCompplete(); dvmmColleectGarrbageIInternnal(GCC_EXPLLICIT); dvmmUnlocckHeapp(); 这个函数数定义在文件件dalviik/vm/allocc/Allooc.cppp中。 如果Daavik虚拟拟机在启动的的时候,通过过-XX:+DisabbleExpplicittGC选项禁禁用了显式GGC,那么函函数dvmCCollecctGarb

20、bage什么么也不做就返返回了。这意意味着Dallvik虚拟拟机可能会不不支持应用程程序显式的GGC请求。 一旦Dallvik虚拟拟机支持显式式GC,那么么函数dvmmColleectGarrbage就就会先锁定堆堆,并且等待待有可能正在在执行的GCC_CONCCURRENNT类型的GGC完成之后后,再调用函函数dvmCCollecctGarbbageInnternaal进行类型型为GC_EEXPLICCIT的GCC。 以上就是是三种情况下下会触发四种种类型的GCC。有了这个个背景知识之之后 ,我们们接下来就开开始分析Daalvik虚虚拟机使用的的Mark-Sweepp算法,整个个过程如图11

21、所示:图1 Dalvvik虚拟机机垃圾收集过过程 Dalvvik虚拟机机支持非并行行和并行两种种GC。在图图1中,左边边是非并行GGC的执行过过程,而右边边是并行GCC的执行过程程。它们的总总体流程是相相似的,主要要差别在于前前者在执行的的过程中一直直是挂起非GGC线程的,而而后者是有条条件地挂起非非GC线程。 由前面的的分析可以知知道,无论是是并行GC,还还是非并行GGC,它们都都是通过函数数dvmCoollecttGarbaageIntternall来执行的。函函数dvmCCollecctGarbbageInnternaal的实现如如下所示:cpp vview pplain copy 在C

22、ODEE上查看代码码片派生到我我的代码片void dvvmColllectGaarbageeInterrnal(cconst GcSpeec* sppec) if (gcHeeap-ggcRunnning) retuurn; gcHHeap-gcRunnning = truue; dvmmSuspeendAlllThreaads(SUUSPENDD_FOR_GC); if (!dvmmHeapBBeginMMarkSttep(sppec-iisParttial) dvmAAbort(); dvmmHeapMMarkRoootSett(); if (specc-isCConcurrrent) d

23、vmCClearCCardTaable(); dvmUUnlockkHeap(); dvmRResumeeAllThhreadss(SUSPPEND_FFOR_GCC); dvmmHeapSScanMaarkedOObjectts(); if (specc-isCConcurrrent) dvmLLockHeeap(); dvmSSuspenndAllTThreadds(SUSSPEND_FOR_GGC); dvmHHeapReeMarkRRootSeet(); dvmHHeapReeScanMMarkeddObjeccts(); dvmmHeapPProcesssRefeerencees(&

24、gccHeap-softtReferrencess, speec-dooPreseerve = fallse, &gccHeap-weakkReferrencess, &gccHeap-finaalizerrReferrencess, &gccHeap-phanntomReeferennces); dvmmHeapSSweepSSystemmWeakss(); dvmmHeapSSourceeSwapBBitmapps(); if (specc-isCConcurrrent) dvmUUnlockkHeap(); dvmRResumeeAllThhreadss(SUSPPEND_FFOR_GC

25、C); dvmmHeapSSweepUUnmarkkedObjjects(spec-isPaartiall, speec-issConcuurrentt, &numOObjecttsFreeed, &nnumByttesFreeed); dvmmHeapFFinishhMarkSStep(); if (specc-isCConcurrrent) dvmLLockHeeap(); dvmmHeapSSourceeGrowFForUtiilizattion(); gcHHeap-gcRunnning = fallse; if (specc-isCConcurrrent) dvmBBroadccas

26、tCoond(&ggDvm.ggcHeappCond); if (!speec-issConcuurrentt) dvmRResumeeAllThhreadss(SUSPPEND_FFOR_GCC); dvmmEnqueeueCleearedRRefereences(&gDvmm.gcHeeap-ccleareedRefeerencees); 这个函数数定义在文件件dalviik/vm/allocc/Heapp.cpp中中。 前面提到到,在调用函函数dvmCCollecctGarbbageInnternaal之前,堆堆是已经被锁锁定了的,因因此这时候任任何需要堆上上分配对象的的线程都会被被挂起

27、。注意意,这不会影影响到那些不不需要在堆上上分配对象的的线程。 在图1中中显示的GCC流程中,除除了第一步的的Lock Heap和和最后一步的的Unlocck Heaap,都对应应于函数dvvmColllectGaarbageeInterrnal的实实现,它的执执行逻辑如下下所示: 第1步到到第3步用于于并行和非并并行GC: 1. 调用函数ddvmSusspendAAllThrreads挂挂起所有的线线程,以免它它们干扰GCC。 2. 调用函数ddvmHeaapBegiinMarkkStep初初始化Marrk Staack,并且且设定好GCC范围。关于于Mark Stackk的介绍,可可以参考

28、前面面一文。 3. 调用函数ddvmHeaapMarkkRootSSet标记根根集对象。 第4到第第6步用于并并行GC: 4. 调用函数ddvmCleearCarrdTablle清理Caard Taable。关关于Cardd Tablle的介绍,可可以参考前面面一文。因为为接下来我们们将会唤醒第第1步挂起的的线程。并且且使用这个CCard TTable来来记录那些在在GC过程中中被修改的对对象。 5. 调用函数ddvmUnllock解锁锁堆。这个是是针对调用函函数dvmCCollecctGarbbageInnternaal执行GCC前的堆锁定定操作。 6. 调用函数ddvmRessumeAll

29、lThreeads唤醒醒第1步挂起起的线程。 第7步用用于并行和非非并行GC: 7. 调用函数ddvmHeaapScannMarkeedObjeects从第第3步获得的的根集对象开开始,归递标标记所有被根根集对象引用用的对象。 第8步到到第11步用用于并行GCC: 8. 调用函数ddvmLocckHeapp重新锁定堆堆。这个是针针对前面第55步的操作。 9. 调用函数ddvmSusspendAAllThrreads重重新挂起所有有的线程。这这个是针对前前面第6步的的操作。 10. 调调用函数dvvmHeappReMarrkRoottSet更新新根集对象。因因为有可能在在第4步到第第6步的执行行

30、过程中,有有线程创建了了新的根集对对象。 11. 调调用函数dvvmHeappReScaanMarkkedObjjects归归递标记那些些在第4步到到第6步的执执行过程中被被修改的对象象。这些对象象记录在Caard Taable中。 第12步到到第14步用用于并行和非非并行GC: 12. 调调用函数dvvmHeappProceessRefferencces处理那那些被软引用用(Softt Refeerencee)、弱引用用(Weakk Refeerencee)和影子引引用(Phaantom Referrence)引引用的对象,以以及重写了ffinaliize方法的的对象。这些些对象都是需需要特

31、殊处理理的。 13. 调调用函数dvvmHeappSweeppSysteemWeakks回收系统统内部使用的的那些被弱引引用引用的对对象。 14. 调调用函数dvvmHeappSourcceSwappBitmaaps交换LLive BBitmapp和Markk Bitmmap。执行行了前面的113步之后,所所有还被引用用的对象在MMark BBitmapp中的bitt都被设置为为1。而Liive Biitmap记记录的是当前前GC前还被被引用着的对对象。通过交交换这两个BBitmapp,就可以使使得当前GCC完成之后,使使得Livee Bitmmap记录的的是下次GCC前还被引用用着的对象。

32、第15步和和第16步用用于并行GCC: 15. 调调用函数dvvmUnloock解锁堆堆。这个是针针对前面第88步的操作。 16. 调调用函数dvvmResuumeAlllThreaads唤醒第第9步挂起的的线程。 第17步和和第18步用用于并行和非非并行GC: 17. 调调用函数dvvmHeappSweeppUnmarrkedObbjectss回收那些没没有被引用的的对象。没有有被引用的对对象就是那些些在执行第114步之前,在在Live Bitmaap中的biit设置为11,但是在MMark BBitmapp中的bitt设置为0的的对象。 18. 调调用函数dvvmHeappFinisshM

33、arkkStep重重置Markk Bitmmap以及MMark SStack。这这个是针对前前面第2步的的操作。 第19步用用于并行GCC: 19. 调调用函数dvvmLockkHeap重重新锁定堆。这这个是针对前前面第15步步的操作。 第20步用用于并行和非非并行GC: 20. 调调用函数dvvmHeappSourcceGrowwForUttilizaation根根据设置的堆堆目标利用率率调整堆的大大小。 第21步用用于并行GCC: 21. 调调用函数dvvmBroaadcasttCond唤唤醒那些等待待GC执行完完成再在堆上上分配对象的的线程。 第22步用用于非并行GGC: 22. 调调用

34、函数dvvmResuumeAlllThreaads唤醒第第1步挂起的的线程。 第23步用用到并行和非非并行GC: 23. 调调用函数dvvmEnquueueClleareddReferrencess将那些目标标对象已经被被回收了的引引用对象增加加到相应的JJava队列列中去,以便便应用程序可可以知道哪些些引用引用的的对象已经被被回收了。 以上就是并并行和非并行行GC的执行行总体流程,它它们的主要区区别在于,前前者在GC过过程中,有条条件地挂起和和唤醒非GCC线程,而后后者在执行GGC的过程中中,一直都是是挂起非GCC线程的。并并行GC通过过有条件地挂挂起和唤醒非非GC线程,就就可以使得应应用程

35、序获得得更好的响应应性。但是我我们也应该看看到,并行GGC需要多执执行一次标记记根集对象以以及递归标记记那些在GCC过程被访问问了的对象的的操作。也就就是说,并行行GC在使用用得应用程序序获得更好的的响应性的同同时,也需要要花费更多的的CPU资源源。 为了更深入入地理解GCC的执行过程程,接下来我我们再详细分分析在上述的的23步中涉涉及到的重要要函数。 1. dvvmSusppendAlllThreeads 函数dvmmSuspeendAlllThreaads用来挂挂起Dalvvik虚拟机机中除当前线线程之外的其其它线程,它它的实现如下下所示:cpp vview pplain copy 在CO

36、DEE上查看代码码片派生到我我的代码片void dvvmSusppendAlllThreeads(SSuspenndCausse whyy) Thrread* self = dvmmThreaadSelff(); Thrread* threaad; locckThreeadSusspend(suspp-all, whyy); dvmmLockTThreaddList(self); locckThreeadSusspendCCount(); forr (thrread = gDvmm.threeadLisst; thhread != NUULL; tthreadd = thhread-nextt

37、) if (threaad = self) contiinue; /* ddebuggger evvents dontt susppend JJDWP tthreadd */ if (why = SUUSPENDD_FOR_DEBUGG | wwhy = SUSPPEND_FFOR_DEEBUG_EEVENT) & threaad-haandle = dvvmJdwppGetDeebugThhread(gDvm.jdwpSState) contiinue; dvmAAddToSSuspenndCounnts(thhread, 1, (wwhy = SUSPPEND_FFOR_DEEBUG |

38、whhy = SUSPEEND_FOOR_DEBBUG_EVVENT) ? 1 : 00); unllockThhreadSSuspenndCounnt(); forr (thrread = gDvmm.threeadLisst; thhread != NUULL; tthreadd = thhread-nextt) if (threaad = self) contiinue; /* ddebuggger evvents dontt susppend JJDWP tthreadd */ if (why = SUUSPENDD_FOR_DEBUGG | wwhy = SUSPPEND_FFOR_

39、DEEBUG_EEVENT) & threaad-haandle = dvvmJdwppGetDeebugThhread(gDvm.jdwpSState) contiinue; /* wwait ffor thhe othher thhread to seee thee pendding ssuspennd */ waittForThhreadSSuspennd(sellf, thhread); dvmmUnlocckThreeadLisst(); unllockThhreadSSuspennd(); 这个函数数定义在文件件dalviik/vm/Threaad.cppp中。 在以下三三种情况下

40、,当当前线程需要要挂起其它线线程: 1. 执执行GC。 2. 调调试器请求。 3. 发发生调试事件件,如断点命命中。 而且,上上述三种情况况有可能是同同时发生的。例例如,一个线线程在分配对对象的过程中中发生GC的的同时,调试试器也刚好请请求挂起所有有线程。这时时候就需要保保证每一个挂挂起操作都是是顺序执行的的,即要对挂挂起线程的操操作进行加锁锁。这是通过过调用函数函函数lockkThreaadSusppend来实实现的。 函数loockThrreadSuuspendd尝试获取ggDvm._threaadSusppendLoock锁。如如果获取失败败,就表明有有其它线程也也正在请求挂挂起Dalv

41、vik虚拟机机中的线程,包包括当前线程程。每一个DDalvikk虚拟机线程程都有一个SSuspennd Couunt计数,每每当它们都挂挂起的时候,对对应的Susspend Countt计数就会加加1,而当被被唤醒时,对对应的Susspend Countt计数就会减减1。在获取取gDvm._threeadSusspendLLock锁失失败的情况下下,当前线程程按照一定的的时间间隔检检查自己的SSuspennd Couunt,直到到自己的Suuspendd Counnt等于0,并并且能成功获获取gDvmm._thrreadSuuspenddLock锁锁为止。这样样就可以保证证每一个挂起起Dalv

42、iik虚拟机线线程的请求都都能够得到顺顺序执行。 从函数llockThhreadSSuspennd返回之后后,就表明当当前线程可以以执行挂起其其它线程的操操作了。它首首先要做的第第一件事情是是遍历Dallvik虚拟拟机线程列表表,并且调用用函数dvmmAddTooSuspeendCouunts将列列表里面的每每一个线程对对应的Susspend Countt都增加1,但但是除了当前前线程之外。注注意,当挂起起线程的请求求是调试器发发出或者是由由调试事件触触发的时候,DDalvikk虚拟机中的的JDWP线线程是不可以以挂起的,因因为JDWPP线程挂起来来之后,就没没法调试了。在在这种情况下下,也不

43、能将将JDWP线线程对应的SSuspennd Couunt都增加加1。 通过调用用函数dvmmJdwpGGetDebbugThrread可以以获得JDWWP线程句柄柄,用这个句句柄和Dallvik虚拟拟机线程列表表中的线程句句柄相比,就就可以知道当当前遍历的线线程是否是JJDWP线程程。同时,通通过参数whhy可以知道道当挂起线程程的请求是否否是由调试器器发出的或者者由调试事件件触发的, 注意,在在增加被挂起起线程的Suuspendd Counnt计数之前前,必须要获获取gDvmm.threeadSusspendCCountLLock锁。这这个锁的获取取和释放可以以通过函数llockThhre

44、adSSuspenndCounnt和unllockThhreadSSuspenndCounnt完成。 将被挂起起线程的Suuspendd Counnt计数都增增加1之后,接接下来就是等等待被挂起线线程自愿将自自己挂起来了了。这是通过过函数waiitForTThreaddSuspeend来实现现。当一个线线程自愿将自自己挂起来的的时候,会将将自己的状态态设置为非运运行状态(TTHREADD_RUNNNING),这这样函数waaitForrThreaadSusppend通过过不断地检查查一个线程的的状态是否处处于非运行状状态就可以知知道它是否已已经挂起来了了。 那么,一一个线程在什什么情况才会会自

45、愿将自己己挂起来呢?一个线程在在执行的过程程中,会在合合适的时候检检查自己的SSuspennd Couunt计数。一一旦该计数值值不等于0,那那么它就知道道有线程请求求挂起自己,因因此它就会很很配合地将自自己的状态设设置为非运行行的,并且将将自己挂起来来。例如,当当一个线程通通过解释器执执行代码时,就就会周期性地地检查自己的的Suspeend Coount是否否等于0。这这里说的周期期性,实际上上就是碰到IIF指令、GGOTO指令令、SWITTCH指令、RRETURNN指令和THHROW指令令等时。 2. dvvmHeappBeginnMarkSStep 函数dvmmHeapBBeginMMa

46、rkSttep用来初初始化堆的标标记范围和MMark SStack,它它的实现如下下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片bool dvvmHeappBeginnMarkSStep(bbool iisParttial) GcMMarkCoontextt *ctxx = &ggDvm.ggcHeapp-marrkConttext; if (!creeateMaarkStaack(&cctx-sstack) retuurn faalse; ctxx-finnger = NULLL; ctxx-immmuneLiimit = (chaar*)d

47、vvmHeappSourcceGetIImmuneeLimitt(isPaartiall); retturn ttrue; 这个函数数定义在文件件dalviik/vm/allocc/MarkkSweepp.cpp中中。 在标记过过程中用到的的各种信息都都保存一个GGcMarkkConteext结构体体描述的GCC标记上下文文ctx中。其其中,ctxx-staack描述的的是Markk Stacck,ctxx-finnger描述述的是一个标标记指纹,在在递归标记对对象时会用到到,ctx-immuuneLimmit用来限限定堆的标记记范围。 函数dvvmHeappBeginnMarkSStep调用

48、用另外一个函函数creaateMarrkStacck来初始化化Mark Stackk,它的实现现如下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片static bool creatteMarkkStackk(GcMaarkStaack *sstack) asssert(sstack != NUULL); sizze_t llengthh = dvvmHeappSourcceGetIIdealFFootprrint() * siizeof(Objecct*) / (sizzeof(OObjectt) + HHEAP_SSOURCEE_CHUNNK_

49、OVEERHEADD); maddvise(stackk-basse, leength, MADVV_NORMMAL); staack-ttop = stackk-basse; retturn ttrue; 这个函数数定义在文件件dalviik/vm/allocc/MarkkSweepp.cpp中中。 函数crreateMMarkSttack根据据最坏情况来来设置Marrk Staack的长度度,也就是用用当前堆的大大小除以对象象占用的最小小内存得到的的结果。当前前堆的大小可可以通过函数数dvmHeeapSouurceGeetIdeaalFoottprintt来获得。在在堆上分配的的对象,都是

50、是从Objeect类继承承下来的,因因此,每一个个对象占用的的最小内存是是sizeoof(Objject)。由由于在为对象象分配内存时时,还需要额额外的内存来来保存管理信信息,例如实实际分配给对对象的内存字字节数。这些些额外的管理理信息占用的的内存字节数数通过宏HEEAP_SOOURCE_CHUNKK_OVERRHEAD来来描述。 由于Maark Sttack实际际上是一个OObjectt指针数组,因因此有了上面面的信息之后后,我们就可可以计算出MMark SStack的的长度lenngth。MMark SStack使使用的内存块块在Dalvvik虚拟机机启动的时候候就创建好的的,具体参考考前

51、面一文,起起始地址位于于stackk-basse中。由于于这块内存开开始的时候被被函数maddvice设设置为MADDV_DONNTNEEDD,因此这里里要将它重新新设置为MAADV_NOORMAL,以以便可以通知知内核,这块块内存要开始始使用了。 Markk Stacck的栈顶sstack-top指指向的是Maark Sttack内存存块的起始位位置,以后就就会从这个位位置开始由小小到大增长。 回到函数数dvmHeeapBegginMarrkStepp中,ctxx-immmuneLiimit记录录的实际上是是堆的起始标标记位置。在在此位置之前前的对象,都都不会被GCC。这个位置置是通过函数数

52、dvmHeeapSouurceGeetImmuuneLimmit来确定定的,它的实实现如下所示示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片void *ddvmHeaapSourrceGettImmunneLimiit(boool isPPartiaal) if (isPaartiall) retuurn hss2heapp(gHs)-basse; eelse retuurn NUULL; 这个函数数定义在文件件dalviik/vm/allocc/HeappSourcce.cppp中。 当参数iisParttial等于于true时时,函数dvvmH

53、eappSourcceGetIImmuneeLimitt返回的是AActivee堆的起始位位置,否则的的话就返回NNULL值。也也就是说,如如果当前执行行的GC只要要求回收部分分垃圾,那么么就只回收AActivee堆的垃圾,否否则的话,就就同时回收AActivee堆和Zyggote堆的的垃圾。 3. ddvmHeaapMarkkRootSSet 函数dvvmHeappMarkRRootSeet用来的标标记根集对象象,它的实现现如下所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片/* Markk the set oof rooot objjects.

54、 * * Thinngs wee needd to sscan: * - Syystem classses deefinedd by rroot cclasslloaderr * - Foor eacch thrread: * - Interrpreteed staack, ffrom ttop too currFramee * - Dallvik rregistters (args + loccal vaars) * - JNI llocal referrencess * - Autommatic VM loocal rrefereences (TracckedAllloc) * - Ass

55、occiatedd Threead/VMMThreaad objject * - ThreaadGrouups (ccould trackk & sttart wwith tthese insteead off workking * upwarrd froom Thrreads) * - Excepption curreently beingg throown, iif preesent * - JNNI gloobal rrefereences * - Innterneed strring ttable * - Prrimitiive cllassess * - Sppeciall obje

56、ects * - gDvm.outOffMemorryObj * - Obbjectss in ddebuggger obbject regisstry * * Dont neeed: * - Naative stackk (forr in-pprogreess sttuff iin thee VM) * - The TTrackeedAllooc stuuff waatchess all nativve VM referrencess. */ void dvvmHeappMarkRRootSeet() GcHHeap *gcHeaap = ggDvm.ggcHeapp; dvmmMarkII

57、mmuneeObjeccts(gccHeap-markkConteext.immmuneLLimit); dvmmVisittRootss(roottMarkOObjecttVisittor, &gcHeaap-maarkConntext); 这个函数数定义在文件件dalviik/vm/allocc/MarkkSweepp.cpp中中。 通过注释释我们可以看看到根集对象象都包含了哪哪些对象。总总的来说,就就是包含两大大类对象。一一类是Dallvik虚拟拟机内部使用用的全局对象象,另一类是是应用程序正正在使用的对对象。前者会会维护在内部部的一些数据据结构中,例例如Hashh表,后者维维护在调用栈

58、栈中。对这些些根集对象进进行标记都是是通过函数ddvmVissitRooots和回调调函数roootMarkkObjecctVisiitor进行行的。 此外,我我们还需要将将不在堆回收收范围内的对对象也看作是是根集对象,以以便后面可以以使用统一的的方法来遍历历这两类对象象所引用的其其它对象。标标记不在堆回回收范围内的的对象是通过过函数dvmmMarkIImmuneeObjeccts来实现现的,如下所所示:cpp vview pplain copy 在CODEE上查看代码码片派生到我我的代码片void dvvmMarkkImmunneObjeects(cconst char *immuuneLi

59、mmit) forr (sizze_t ii = 1; i numHeeaps; +i) if (gHs-heapssi.bbase heaapsi.limiit heapssi.bbase - gHs-liveeBits.base); /* Coomputee the startting ooffsett in tthe liive annd marrk bitts. */ char *src = (chhar *)(gHs-liveeBits.bits + inddex); char *dst = (chhar *)(gHs-markkBits.bits + inddex); /* Coo

60、mputee the numbeer of bytess of tthe liive biitmap to coopy. */ size_t lenngth = HB_OOFFSETT_TO_BBYTE_IINDEX( ggHs-hheapsi.liimit - gHs-heappsi.base); /* Doo the copy. */ memcppy(dstt, srcc, lenngth); /* Maake suure maax poiints tto thee addrress oof thee highhest sset biit. */ if (ggHs-mmarkBiits.m

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论