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)过程分析
相关文章:
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 eno…...

opencv读入图片注意事项
来源:投稿 作者:蓬蓬奇 编辑:学姐 深度学习数据预处理中常用opencv读入图片,一般在__getitem__函数中调用。本文主要介绍opencv读取图片的一些细节以及注意事项。本文中使用的图片见第6节“opencv测试使用的图片”。 1.如何通过o…...

学习渗透测试,考CISP-PTE还是考NISP-PT证书呢?
其实两者都可以,但是要看考生的实际需求! 为什么说两者都可以? 两个证书都由中国信息安全测评中心颁发,CISP-PTE(注册信息安全渗透测试工程师),NISP-PT(国家信息安全水平考试渗透测试工程师),…...

记录自己遇到的关于Hashmap的面试题
一.麻烦讲述一下Hashmap的扩容原理 jdk1.8中的hashmap扩容原理 1.put流程图 首先贴一张图(图片来源于传送门),多谢大佬的美图,此图已经完美的描述了put的整个流程,我也就不想自己画了,嘿嘿: 2.hashmap中几个比较重…...

mysql数据库之sql语句性能分析工具
一、sql执行频率。 mysql客户端连接成功后,通过show [session | global] status 命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT/UPDATE/DELETE的访问频次。 #一个下划线代表一个字符 show global status like com_; 二、慢查…...

搭建SpringBoot项目
文章目录前言准备工具创建项目前言 为什么使用SpringBoot?它有什么好处? SpringBoot可以快速构建出独立的Spring应用,简化了配置文件。内嵌Tomcat服务器,无须手动部署war文件。 准备工具 idea2022navicat16postmanjdk1.8 创建项目 File-&…...

“一网统管”视频融合平台EasyCVR页面tab切换细节优化
EasyCVR视频融合平台基于云边端协同架构,能支持海量视频的轻量化接入与汇聚管理,借助大数据分析的决策判断,为网络摄像头、网络存储设备、智能终端、无人机、车载设备、移动执法仪、视频监控平台等提供一体化的视频接入、分发、存储、处理等能…...
【Python入门第二十天】Python Lambda
lambda 函数是一种小的匿名函数。 lambda 函数可接受任意数量的参数,但只能有一个表达式。 语法 lambda arguments : expression执行表达式并返回结果: 实例 一个 lambda 函数,它把作为参数传入的数字加 10,然后打印结果&…...

比特数据结构与算法(第四章_下)二叉树OJ(力扣:144,965,104,226,100,572)
144. 二叉树的前序遍历难度简单给你二叉树的根节点 root ,返回它节点值的 前序 遍历。示例 1:输入:root [1,null,2,3]输出:[1,2,3]示例 2:输入:root [ ]输出:[ ]示例 3:输入&#…...

【C++】inline 内联函数
文章目录📕 概念📕 使用前的准备📕 使用📕 特性📕 概念 在 C 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表…...

如何审计一个智能合约
智能合约审计用于整个 DeFi 生态系统,通过对协议代码的深入审查,可以帮助解决识别错误、低效代码以及这些问题。智能合约具有不可篡改的特点,这使得审计成为任何区块链项目安全流程的关键部分。 代码审计对任何应用程序都很重要,…...

不用PS,也能实现抠图的工具
对于非设计专业的同学来说,专门下载 PS 抠图有点大材小用,而且运用 PS 对电脑配置一定要求。不过现在有了更多选择,市面上出现了越来越多的抠图软件,不过越多的抠图软件选择也意味着需要花费时间试错因此本文将给大家推荐 3 款非常…...
集群化存储的概述
集群化存储的概述 1、存储的分类方式: 存储的分类-网络拓扑 用于存储的网络拓扑 NAS:小米路由器;SAN:存储区网络–>网络网和存储网络区分开DAS:常见的存储;本地存储 存储分类-存储技术网络拓扑存储技…...
asyncio 并发编程(一)
Python2 时代高性能的网络编程主要是 Twisted、Tornado 和 Gevent 这三个库,但是它们的异步代码相互之间既不兼容也不能移植。Gvanrossum 希望在 Python 3 实现一个原生的基于生成器的协程库,其中直接内置了对异步 IO 的支持,这就是 asyncio&…...
春招冲刺(二):BFC 盒子面试题总结
BFC 盒子面试题总结 Q1:BFC盒子是什么? BFC全称是Block Formatting Context 意思就是块级格式化上下文。 可以把BFC看做一个容器,容器里边的元素不会影响到容器外部的元素。 Q2:如何创建BFC? 根元素:bo…...

Ep_计网面试题-本地IP地址怎么一层层向上转换?
将数据加上报头打包在一起形成新的数据包继续往下一层传递。拆包的时候就是把数据包去掉包头作为新数据传给上一层 视频讲解: https://edu.csdn.net/course/detail/38090 点我进入 面试宝典 很多人不知道面试问什么,或者其他的XXGuide,那里边的太多没用的,也没有源码解析,都…...

MySQL高级三
目录 三、MySQL高级03 3.1 MyCat 3.1.1 MyCat简介 3.1.2 中间件的作用 3.2 安装MyCat 3.3 主从复制 3.3.1 主从复制的原理 3.3.2 主从复制的好处 3.3.3 配置主从复制 三、MySQL高级03 如果虚拟机的磁盘已满,可以对磁盘进行重新分配 参考:虚拟…...

set和map的基本使用
目录 关联式容器 要点分析 键值对 pair介绍 set 模板参数列表: set的构造: 常用接口 操作 multiset map map的构造 插入 make_pair map的迭代器 operator[] multimap multimap中为什么没有重载operator[] 关联式容器 关联式容器也是用…...

已解决pip install wxPython模块安装失败
已解决(pip install wxPython安装失败)error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above,not pip. hint : See above for output from …...

Linux基础——连接Xshell7
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。个人主页:小李会科技的…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...