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

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...