当前位置: 首页 > 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)过程分析


相关文章:

Android Dalvik虚拟机 GC流程分析

前言 本篇继续介绍安卓dalvik虚拟机&#xff0c;介绍Dalvik虚拟技的GC流程。 GC结构体 dalvik/vm/alloc/Heap.h static const GcSpec kGcForMallocSpec {true, /* isPartial */false, /* isConcurrent */true, /* doPreserve */"GC_FOR_ALLOC" }; /* Not eno…...

opencv读入图片注意事项

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

学习渗透测试,考CISP-PTE还是考NISP-PT证书呢?

其实两者都可以&#xff0c;但是要看考生的实际需求&#xff01; 为什么说两者都可以&#xff1f; 两个证书都由中国信息安全测评中心颁发&#xff0c;CISP-PTE&#xff08;注册信息安全渗透测试工程师&#xff09;,NISP-PT&#xff08;国家信息安全水平考试渗透测试工程师),…...

记录自己遇到的关于Hashmap的面试题

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

mysql数据库之sql语句性能分析工具

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

搭建SpringBoot项目

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

“一网统管”视频融合平台EasyCVR页面tab切换细节优化

EasyCVR视频融合平台基于云边端协同架构&#xff0c;能支持海量视频的轻量化接入与汇聚管理&#xff0c;借助大数据分析的决策判断&#xff0c;为网络摄像头、网络存储设备、智能终端、无人机、车载设备、移动执法仪、视频监控平台等提供一体化的视频接入、分发、存储、处理等能…...

【Python入门第二十天】Python Lambda

lambda 函数是一种小的匿名函数。 lambda 函数可接受任意数量的参数&#xff0c;但只能有一个表达式。 语法 lambda arguments : expression执行表达式并返回结果&#xff1a; 实例 一个 lambda 函数&#xff0c;它把作为参数传入的数字加 10&#xff0c;然后打印结果&…...

比特数据结构与算法(第四章_下)二叉树OJ(力扣:144,965,104,226,100,572)

144. 二叉树的前序遍历难度简单给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。示例 1&#xff1a;输入&#xff1a;root [1,null,2,3]输出&#xff1a;[1,2,3]示例 2&#xff1a;输入&#xff1a;root [ ]输出&#xff1a;[ ]示例 3&#xff1a;输入&#…...

【C++】inline 内联函数

文章目录&#x1f4d5; 概念&#x1f4d5; 使用前的准备&#x1f4d5; 使用&#x1f4d5; 特性&#x1f4d5; 概念 在 C 中&#xff0c;为了解决一些频繁调用的小函数大量消耗栈空间&#xff08;栈内存&#xff09;的问题&#xff0c;特别的引入了 inline 修饰符&#xff0c;表…...

如何审计一个智能合约

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

不用PS,也能实现抠图的工具

对于非设计专业的同学来说&#xff0c;专门下载 PS 抠图有点大材小用&#xff0c;而且运用 PS 对电脑配置一定要求。不过现在有了更多选择&#xff0c;市面上出现了越来越多的抠图软件&#xff0c;不过越多的抠图软件选择也意味着需要花费时间试错因此本文将给大家推荐 3 款非常…...

集群化存储的概述

集群化存储的概述 1、存储的分类方式&#xff1a; 存储的分类-网络拓扑 用于存储的网络拓扑 NAS&#xff1a;小米路由器&#xff1b;SAN&#xff1a;存储区网络–>网络网和存储网络区分开DAS&#xff1a;常见的存储&#xff1b;本地存储 存储分类-存储技术网络拓扑存储技…...

asyncio 并发编程(一)

Python2 时代高性能的网络编程主要是 Twisted、Tornado 和 Gevent 这三个库&#xff0c;但是它们的异步代码相互之间既不兼容也不能移植。Gvanrossum 希望在 Python 3 实现一个原生的基于生成器的协程库&#xff0c;其中直接内置了对异步 IO 的支持&#xff0c;这就是 asyncio&…...

春招冲刺(二):BFC 盒子面试题总结

BFC 盒子面试题总结 Q1&#xff1a;BFC盒子是什么&#xff1f; BFC全称是Block Formatting Context 意思就是块级格式化上下文。 可以把BFC看做一个容器&#xff0c;容器里边的元素不会影响到容器外部的元素。 Q2&#xff1a;如何创建BFC&#xff1f; 根元素&#xff1a;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 如果虚拟机的磁盘已满&#xff0c;可以对磁盘进行重新分配 参考&#xff1a;虚拟…...

set和map的基本使用

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

已解决pip install wxPython模块安装失败

已解决&#xff08;pip install wxPython安装失败&#xff09;error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above&#xff0c;not pip. hint : See above for output from …...

Linux基础——连接Xshell7

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

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...