当前位置: 首页 > 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;小李会科技的…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...