当前位置: 首页 > news >正文

Android Dalvik虚拟机 GC流程分析

前言

本篇继续介绍安卓dalvik虚拟机,介绍Dalvik虚拟技的GC流程。

GC结构体

  • dalvik/vm/alloc/Heap.h
static const GcSpec kGcForMallocSpec = {true,  /* isPartial */false,  /* isConcurrent */true,  /* doPreserve */"GC_FOR_ALLOC"
};
/* Not enough space for an "ordinary" Object to be allocated. */
const GcSpec *GC_FOR_MALLOC = &kGcForMallocSpec;static const GcSpec kGcConcurrentSpec  = {true,  /* isPartial */true,  /* isConcurrent */true,  /* doPreserve */"GC_CONCURRENT"
};
/* Automatic GC triggered by exceeding a heap occupancy threshold. */
const GcSpec *GC_CONCURRENT = &kGcConcurrentSpec;static const GcSpec kGcExplicitSpec = {false,  /* isPartial */true,  /* isConcurrent */true,  /* doPreserve */"GC_EXPLICIT"
};
/* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
const GcSpec *GC_EXPLICIT = &kGcExplicitSpec;static const GcSpec kGcBeforeOomSpec = {false,  /* isPartial */false,  /* isConcurrent */false,  /* doPreserve */"GC_BEFORE_OOM"
};
/* Final attempt to reclaim memory before throwing an OOM. */
const GcSpec *GC_BEFORE_OOM = &kGcBeforeOomSpec;

gcDemonThread启动

虚拟机启动时会初始化gcDemonThread,等待被唤醒调用,主要执行concurrent gc。
有两个时机:

  • 一是主动调用dvmSignalCond(&gHs->gcThreadCond);唤醒锁,此处是在分配对象时超过concurrentStartBytes时调用;
  • 二是超时唤醒,此时会执行trimHeaps,向系统归还虚拟内存和物理内存。
/** The garbage collection daemon.  Initiates a concurrent collection* when signaled.  Also periodically trims the heaps when a few seconds* have elapsed since the last concurrent GC.*/
static void *gcDaemonThread(void* arg)
{dvmChangeStatus(NULL, THREAD_VMWAIT);dvmLockMutex(&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 = true;}} else {dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);}if (gDvm.debuggerConnected) {continue;}dvmLockHeap();/** Another thread may have started a concurrent garbage* collection before we were scheduled.  Check for this* condition before proceeding.*/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);return NULL;
}

GC流程

调用dvmCollectGarbageInternal方法,进行各种类型的GC过程。

  • concurrent gc会dvmSuspendAllThreads两次,但每次耗时短,整体对app运行影响不大,代码中分位了suspend A 和 suspend B。
  • malloc gc会一直dvmSuspendAllThreads,是stop the world类型GC,会造成app卡顿。
void dvmCollectGarbageInternal(const GcSpec* spec) {if (gcHeap->gcRunning) {return;}gcHeap->gcRunning = true;// GC开始时间rootStart = dvmGetRelativeTimeMsec();// 挂起除gc以外所有线程dvmSuspendAllThreads(SUSPEND_FOR_GC);  // Suspend A// If we are not marking concurrently raise the priority of the thread performing the garbage collection. 非并发gc则提高线程优先级if (!spec->isConcurrent) {oldThreadPriority = os_raiseThreadPriority();}// Verifying roots and heap before GC,检测roots是否有效if (gDvm.preVerify) {verifyRootsAndHeap();}// 创建GcMarkStack,isPartial为true则只回收heap[0]堆的内存dvmHeapBeginMarkStep(spec->isPartial);// Mark the set of objects that are strongly reachable from the roots. 搜集根节点dvmHeapMarkRootSet();// 并发gc在这里释放锁,Suspend A阶段完成if (spec->isConcurrent) {// Resume threads while tracing from the roots.  We unlock the heap to allow mutator threads to allocate from free space.dvmClearCardTable();dvmUnlockHeap();dvmResumeAllThreads(SUSPEND_FOR_GC); // Suspend ArootEnd = dvmGetRelativeTimeMsec();  // 阶段A耗时}// Recursively mark any objects that marked objects point to strongly. If we're not collecting soft references, soft-reachable objects will also be marked.// 以markbits中标记的root引用开始,采用递归的方法把所有对象的强引用对象都在markbits里标记上,同时将这些对象压入GcMarkStack中dvmHeapScanMarkedObjects();// 并发gc再收集一遍,主要是cardTable这里。cardTable:为了记录在垃圾收集过程中对象的引用情况的,以便可以实现Concurrent GCif (spec->isConcurrent) {// Re-acquire the heap lock and perform the final thread suspension.dirtyStart = dvmGetRelativeTimeMsec();dvmLockHeap();dvmSuspendAllThreads(SUSPEND_FOR_GC);  // Suspend BdvmHeapReMarkRootSet();// With the exception of reference objects and weak interned strings, all gray objects should now be on dirty cards.if (gDvm.verifyCardTable) {dvmVerifyCardTable();}// Recursively mark gray objects pointed to by the roots or by heap objects dirtied during the concurrent mark. 这里从cardTable里遍历被标记为dirty的元素dvmHeapReScanMarkedObjects();}// All strongly-reachable objects have now been marked.  Process weakly-reachable objects discovered while tracing. Process reference class instances and schedule finalizations. 收集一些弱引用了;dvmHeapProcessReferences(&gcHeap->softReferences,spec->doPreserve == false,&gcHeap->weakReferences,&gcHeap->finalizerReferences,&gcHeap->phantomReferences);// Process all the internal system structures that behave like weakly-held objects. 收集内部的一些弱引用的变量,如jni的弱引用dvmHeapSweepSystemWeaks();// 交换liveBits和markBits,因为现在markBits保存的是GC后的对象而liveBits还是GC以前的,因此直接交换两者,这样就不用再花时间去重建liveBits了dvmHeapSourceSwapBitmaps();// 用新的livebits去检查引用是否有效if (gDvm.postVerify) {verifyRootsAndHeap();}if (spec->isConcurrent) {dvmUnlockHeap();dvmResumeAllThreads(SUSPEND_FOR_GC); dirtyEnd = dvmGetRelativeTimeMsec(); // 并发回收阶段Suspend B结束}// Walk through the list of objects that haven't been marked and free them.  Assumes the bitmaps have been swapped. 前面收集完成了,clear所有未标注对象。dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent, &numObjectsFreed, &numBytesFreed);// 释放markBits 和 GcMarkStack栈dvmHeapFinishMarkStep();if (spec->isConcurrent) {dvmLockHeap();}/* Now's a good time to adjust the heap size, since* we know what our utilization is.** This doesn't actually resize any memory;* it just lets the heap grow more when necessary.*/// 每次gc后,尝试着去调整堆大小,按照已分配内存 / 堆利用率 去调整堆大小dvmHeapSourceGrowForUtilization();currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);if (spec->isConcurrent) {// 唤醒所有堆的锁dvmBroadcastCond(&gDvm.gcHeapCond);}// 同步的回收此处才是Suspend A结束点if (!spec->isConcurrent) {dvmResumeAllThreads(SUSPEND_FOR_GC);dirtyEnd = dvmGetRelativeTimeMsec(); // Suspend Aif (oldThreadPriority != INT_MAX) {os_lowerThreadPriority(oldThreadPriority);}}// 触发被回收对象的referenceQueuedvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);gcEnd = dvmGetRelativeTimeMsec(); // 一次gc运行总耗时,pause为真正suspendAll的耗时// 打印日志percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);if (!spec->isConcurrent) {u4 markSweepTime = dirtyEnd - rootStart;u4 gcTime = gcEnd - rootStart;bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums, total %ums",spec->reason,isSmall ? "<" : "",numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,percentFree,currAllocated / 1024, currFootprint / 1024,markSweepTime, gcTime);} else {u4 rootTime = rootEnd - rootStart;u4 dirtyTime = dirtyEnd - dirtyStart;u4 gcTime = gcEnd - rootStart;bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums+%ums, total %ums",spec->reason,isSmall ? "<" : "",numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,percentFree,currAllocated / 1024, currFootprint / 1024,rootTime, dirtyTime, gcTime);}
}
  • dalvik/vm/alloc/MarkSweep.cpp
/* 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 vars)*   - 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 string table* - Primitive classes* - Special objects*   - gDvm.outOfMemoryObj* - Objects in debugger object registry** Don't need:* - Native stack (for in-progress stuff in the VM)*   - The TrackedAlloc stuff watches all native VM references.*/
void dvmHeapMarkRootSet()
{GcHeap *gcHeap = gDvm.gcHeap;dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
}

Dalvik虚拟机垃圾收集(GC)过程分析


http://www.lryc.cn/news/25291.html

相关文章:

  • opencv读入图片注意事项
  • 学习渗透测试,考CISP-PTE还是考NISP-PT证书呢?
  • 记录自己遇到的关于Hashmap的面试题
  • mysql数据库之sql语句性能分析工具
  • 搭建SpringBoot项目
  • “一网统管”视频融合平台EasyCVR页面tab切换细节优化
  • 【Python入门第二十天】Python Lambda
  • 比特数据结构与算法(第四章_下)二叉树OJ(力扣:144,965,104,226,100,572)
  • 【C++】inline 内联函数
  • 如何审计一个智能合约
  • 不用PS,也能实现抠图的工具
  • 集群化存储的概述
  • asyncio 并发编程(一)
  • 春招冲刺(二):BFC 盒子面试题总结
  • Ep_计网面试题-本地IP地址怎么一层层向上转换?
  • MySQL高级三
  • set和map的基本使用
  • 已解决pip install wxPython模块安装失败
  • Linux基础——连接Xshell7
  • C++——智能指针1
  • [数据集][VOC][目标检测]翻越栏杆翻越防护栏数据集目标检测可用yolo训练-1035张介绍
  • 深度学习 | BN层原理浅谈
  • 每日面试题
  • 将IDEA的项目托管到gitee
  • 父类子类静态代码块、构造代码块、构造方法执行顺序
  • 【C++】开散列实现unordered_map与unordered_set的封装
  • 华为OD机试真题Python实现【删除指定目录】真题+解题思路+代码(20222023)
  • CSS选择器大全(上)
  • JavaScript 俄罗斯方块 - setTimeout和rAF
  • LeetCode:构造最大二叉树;使用中序和后序数组构造二叉树;使用前序和中序数组遍历二叉树。