DVM GC源码研究

Android内存优化(二)DVM和ART的GC日志分析 在Java虚拟机(三)垃圾标记算法与Java对象的生命周期这篇文章中,提到了Java虚拟机的GC日志。DVM和ART的GC日志与Java虚拟机的日志有较大的区别,这篇文章就对DVM和ART的GC日志进行分析。 阅读详情

基于KK的代码。本文主要是介绍dalvik GC的一些概况。

DalvikGC概要

Davik GC的主要对象

  • GCHeap : 管理GC内存的对象,全局只有一个,负责GC的分配和回收
  • HeapSource: 管理Heap的对象。在GC初始化时,分配一段连续的地址空间(maximumSize), 负责管理Heap的增长
  • Heap: 为分配准备的连续空间。HeapSource包含两个Heap
  • HeapBitmap: 对Heap做mark映射的bitmap对象。HeapSource包含两个bitmap:liveBits和markBits
  • GcMarkStack: 在Mark阶段使用到的堆栈

Dalvik GC的堆分布情况


GC的堆的创建

在GC初始化时,HeapSource会创建一段连续空间,作为堆来使用。
这是由dvmHeapSourceStartup完成的。
部分代码如下:
</pre><pre name="code" class="cpp">GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
                             size_t growthLimit)
{
....
    /*
     * Allocate a contiguous region of virtual memory to subdivided
     * among the heaps managed by the garbage collector.
     */
    length = ALIGN_UP_TO_PAGE_SIZE(maximumSize); //获取的是堆的最大值
    base = dvmAllocRegion(length, PROT_NONE, gDvm.zygote ? "dalvik-zygote" : "dalvik-heap");
    //创建了一个连续地址空间。但是并没有实际的内存分配出来
    if (base == NULL) {
        return NULL;
    }

    /* Create an unlocked dlmalloc mspace to use as
     * a heap source.
     */
    //这个创建分配器
    msp = createMspace(base, kInitialMorecoreStart, startSize);
    if (msp == NULL) {
        goto fail;
    }
.....
    //初始化heap
    if (!addInitialHeap(hs, msp, growthLimit)) {
        LOGE_HEAP("Can't add initial heap");
        goto fail;
    }
....
}

//addInitalHeap
static bool addInitialHeap(HeapSource *hs, mspace msp, size_t maximumSize)
{
    assert(hs != NULL);
    assert(msp != NULL);
    if (hs->numHeaps != 0) {
        return false;
    }
    hs->heaps[0].msp = msp;
    hs->heaps[0].maximumSize = maximumSize;
    hs->heaps[0].concurrentStartBytes = SIZE_MAX;
    hs->heaps[0].base = hs->heapBase;
    hs->heaps[0].limit = hs->heapBase + maximumSize;
    hs->heaps[0].brk = hs->heapBase + kInitialMorecoreStart; //这是初次大小,以后按照这个递增
    hs->numHeaps = 1;
    return true;
}

这个函数只会在zygote中调用。dvm为zygote保留了一段连续地址空间,但是并没有分配内存,当需要的时候,使用dvmHeapSourceAllocAndGrow来增长。

App的内存堆布局

App的堆布局有所不同,如下图:


HeapSource是dalvik中管理堆对象的对象。HeapSource定义了成员Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];

HEAP_SOURCE_MAX_HEAP_COUNT = 2。 heaps[0]永远是当前活动Heap, 即承担新对象分配的heap,而heap[1]则是zygote分配的对象。


事情是这样的,当zygote要fork一个新进程时,ZygoteInit.preFrok会在fork之前调用,该函数最终会调用到dalvik的dvmHeapSourceStartupBeforeFork, 然后调用addNewHeap。

addNewHeap会在原heap[0]的剩余空间创建一个新的heap,然后将heap[0] copy 到heap[1],新的heap copy到heap[0]。 然后再fork。


当然,这个过程只会在第一次fork时执行,此后就不会被执行了。


我们知道,zygote进程在启动后,会preload大量的classes, 资源,这些都会存放在zygote的space中,当zygote fork出新进程后,新进程就继承了这些资源。新进程的对象,则在新创建的heap上。


Dalvik GC 的Alloctor


dvm的GC Allocator是使用外部的dl malloc的 mspace来管理的。这个在libc中定义和实现。

在 dvm dvmHeapSourceStartup函数中调用create_mspace_with_base,在heap上传教对应的对象。

mspace的函数有

  • create_mspace_with_base
  • mspace_malloc/mspace_calloc
  • mspace_bulk_free
  • mspace_inspect_all

这些函数的用法和简单,具体的实现可以在看对应源码。


Dalvik GC过程


Dalvik GC的启动方式及入口

dvmCollectGarbageInternal 函数是所有类型GC的入口。

GC的启动方式有如下几种:

  1. GC_FOR_MALLOC: 当分配空间时,发现空间不足
  2. GC_CONCURRENT: 以并发方式启动GC
  3. GC_EXPLICIT: 通过system.runtime.gc调用的GC
  4. GC_BEFORE_OOM:OOM之前的GC

每种GC的处理方式又不同,根据它们的配置GcSpec数据的定义,可以用下表俩表示

并行清理软引用
FOR_MALLOC T F T
CONCURRENT T T T
EXPLICIT F T T
OOM F FF
说明 app heap被回收
zygote heap不回收 执行并行F时执行软引用

GC类型APP Heap不足并行清理软引用
FOR_MALLOC 是 否否
CONCURRENT是是否
EXPLICIT否是否
OOM否否否

  1. AppHeap不足:描述了GC 引起的原因里面是否包含了App Heap不足,即便不是App Heap引起的,也会清理App Heap;
  2. 并行:是否以并行方式GC
  3. 清理引用:是否清理引用。

Dalvik GC使用的算法

DVM 使用mark-sweep算法。

HeapSource 定义了HeapBitmap liveBits和markBits来标记bitmap。

make sweep算法简介

简单的mark sweep算法 (2色法)


这个过程是:
  1. 新分配对象都是白色 (liveBits中做标记)
  2. 从root对象开始,遍历每个root对象,对每个对象做mark,使之变成黑色(markBits中做标记),同时将对象加入worklist中
  3. 遍历worklist中的对象的白色子对象,对每个白色子对象做mark,使之变成黑色,同时将该子对象加入worklist。
  4. worklist空后,就结束了mark过程
  5. 将所有白色对象清除(liveBits中有标记但是markBits中没有标记的对象)
  6. 黑色变为白色(用markBits替换liveBits)

增量mark-sweep算法 (3色法)


与2色法不同在于,当一个对象被mark后,不是变为黑色,而是变为灰色,这是该对象还在worklist中;当从worklist中取出该对象,并mark了它的所有子对象后,它才会变成黑色。

灰色表示对象本身已经被mark,但是其子对象没有被mark。

这种方法用于并行mark。方法是
  1. mark roots时,暂停所有java线程,完成roots mark后,重启所有java线程
  2. 并行进行mark栈中对象
  3. 当java线程修改对象后,将被修改对象重新标记为gray (write barrier)
  4. 栈空后,再次暂停所有java线程, 重新mark gray对象
  5. 重启java线程对象
  6. sweep

dalvik中的 mark-sweep算法实现

dvmHeapMarkRootSet

mark root对象,root对象包括:
  • 由root classloader定义的系统class
  • 对于每个线程:
    • interpreted stack, from top to "curFrame"
      • davik registers (args + local vars)
    • JNI local references
    • Automatic VM local reference (TrackedAlloc)
    • Associated Thread/VMThread object
    • ThreadGroups (could track & start with these instead of working upward from threads)
    • JNI global references
    • 内部string table
    • gDvm.outOfMemoryObj
    • Objects in debugger object registry

dvmHeapScanMarkedObjects

该函数首先调用
dvmHeapBitmapScanWalk(ctx->bitmap, scanBitmapCallback, ctx);
ctx是类型GcMarkContext, ctx->bitmap是heapsource的markBits,这是保存的都是root对象。

这种方法省去了worklist。
scanBitmapCallback函数扫描每个对象的子对象,然后将他们mark后加入到堆栈中。

然后调用 processMarkStack。该函数访问堆栈中的每个对象的子对象,然后再将子对象放入堆栈中。知道堆栈为空,就完成了扫描。
堆栈是用GcMarkStack对象实现的,该类用一个object* 数组来作为栈。

实现mark的是函数 markObjectNonNull。

dvmHeapProcessReferences

该函数主要负责处理各种引用,包括softRefereces, weakReferences, phatomReferences,
并且会调用finalize函数。

dvmHeapSweepUnmarkedObjects

该函数负责清除垃圾对象。最终负责释放对象的是函数dvmHeapSourceFreeList,也是用mspace来实现的。有兴趣的可以自己看看。

dvm 的并行GC

请考虑一个问题,如果当前正在 dvmHeapScanMarkedObjects,  其中某个对象(加入是A对象)已经变成黑色了,这时某个java线程修改了A的某个field,改变了它的reachable树。这个时候,我们需要知道这个A对象已经被修改了,当dvmHeapScanMarkedObjects完成后,我们需要再次对这些对象进行Remark。
那么,我们怎么知道那些对象被修改了呢?

需要两个东西:CardTable和write barrier。

cardtable
如下图:

内存被分为若干card (如512字节为一个card)
CardTable中每一个小格代表一个card(1字节)。如果被设置,表示对应的card内的对象被修改,需要重新mark
在iput-object这样的指令插入对card table的修改

DVM中dvmCardTableStartup 初始化 card table。 每个card大小为GC_CARD_SIZE (1<<7)。catd table 为每个card映射一个byte,该byte值是GC_CARD_CLEAN或者GC_CARD_DIRTY
  1. dvmCardFromAddr: 从addr得到card的标志字节
  2. dvmMarkCard: 将card置脏
write barrier

dvmSetFieldObject: write barrier,调用dvmMarkCard,将card table置脏。这个函数在dvm内部和jni函数SetFieldObject中会被调用。

在虚拟机内部,则通过IPUT_OBJECT指令的汇编函数来实现:

.LOP_IPUT_OBJECT_finish:
....
    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base                                                                                                                                               
...
    cmp     r0, #0                      @ stored a null reference?
    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not

//r2地址最低字节正好是CARD_DIRTY值。
为了节省cpu,dvm故意将cardtable数组的前0x7f大小的位置空出,从0x7f后开始计算,于是,他就可以用cartable数组的地址的低8位(r2)来保存标志值了。




Android内存优化(一)DVM和ART原理初探 要学习Android的内存优化,首先要了解Java虚拟机,此前我用了多篇文章来介绍Java虚拟机的知识,就是为了这个系列做铺垫。在Android开发中我们接触的是与Java虚拟机类似的Dalvik虚拟机和ART虚拟机,这一篇我们就来了解它们的基本原理。 阅读详情

相关推荐

Java虚拟机、Minot GC、Full GC机制的总结(完整版)

JVM结构图 类加载器把字节码文件(.class)文件加载JVM虚拟机中的过程 以上是Java虚拟机的结构,和编译加载流程。 JVM内存管理 Java栈区: 作用: 它存放的是Java方法执行时的所有的数据 组成: 由栈帧组成,一个栈帧代表一个方法的执行 Java栈帧: 每个方法从调用到执行完成就对应一个栈帧,在虚拟机中入栈到出栈。(局部变量、栈操作数、动态连接、方法出口) 本地方法栈...

weixin_39079048的博客 1019

VPP创建主HEAP内存

初始化clib_mem_vm_map_hdr_t结构的hdr,将其作为新的内存映射尾部赋值给last_map,初始化hdr的起始地址,页面大小和数量等信息。base起始地址开头为clib_mem_heap_t结构,进行初始化,随后跳过此结构以及名称长度的空间,将随后空间生成mspace结构,以供mspace_malloc分配内存使用,例如pool_get的内存分配。在函数clib_mem_vm_map_internal中,首先设置mmap标志MAP_SHARED,mmap的空间变动在其它进程中可见。

redwingz的博客 1466

Dalvik源码

在SDKandroid中是不能看Dalvik的,想看还需要!有需要的可以下载

虚拟机中内存管理

什么是Heap(堆) 在Dalvik虚拟机中,堆实际上就是一块匿名共享内存. 一个mspace(libc中的概念),可以通过libc提供的函数create_mspace_with_base创建一个mspace,然后再通过mspace_开头的函数管理该mspace。例如,我们可以通过mspace_malloc和mspace_bulk_free来在指定的mspace中分配和释放内存。 函数dvmAll...

gbmaotai的博客 1057

Jetbrains IDE 配置上Gemini Code 插件,让你的的编程高效起飞~

Gemini Code Assist是 Google 家产品,基座模型为最新的Gemini 2.5 Pro。本篇实践了在 WebStrom 中Gemini Code Assist插件的高效编程。

2301_76993408的博客 4596

【R语言实例】物种分布模型介绍

SDM介绍

wong2016的博客 1万+

Malloc函数

C++语言: Doug Lea的malloc实现源码(头文件)/*          头文件 malloc.h                       *//*  Default header file for malloc-2.8.x, written by Doug Lea  and released to the public domain, as explained at  htt

suxiaolincalendar的专栏 5233

别再手动挂载NPU了!手把手教你用Ascend-Docker-Runtime一键启动昇腾AI容器

本文详细介绍了如何使用Ascend-Docker-Runtime一键启动昇腾AI容器,大幅简化NPU设备挂载流程。通过环境变量ASCEND_VISIBLE_DEVICES,开发者可以灵活指定NPU设备,提升开发效率。文章还提供了安装配置指南、高级使用技巧和常见问题排查方法,帮助开发者快速掌握这一高效工具。

weixin_42662605的博客 253

Dalvik虚拟机Java堆创建过程分析

使用C/C++开发应用程序最令头痛的问题就是内存管理。慎不留神,要么内存泄漏,要么内存破坏。虚拟机要解决的问题之一就是帮助应用程序自动分配和释放内存。为了达到这个目的,虚拟机在启动的时候向操作系统申请一大块内存当作对象堆。之后当应用程序创建对象时,虚拟机就会在堆上分配合适的内存块。而当对象不再使用时,虚拟机就会将它占用的内存块归还给堆。Dalvik虚拟机也不例外,本文就分析它的Java堆创建过程。

老罗的Android之旅 2万+

Uni-App推送踩坑实录:从云函数URL化到后台推送失效,我都经历了什么

本文详细记录了Uni-App开发者在接入uni-push 2.0时遇到的推送失效问题,从云函数URL化到后台推送失效的完整排查过程。通过系统化分析和与官方沟通,最终找到解决方案,并分享了预防措施与最佳实践,帮助开发者避免类似问题。

weixin_42552716的博客 487

宝马项目化流程标准(BMW ABC flyer requirement)

宝马项目化流程标准(BMW ABC flyer requirement)

于大博 7252

DVM和ART

【代码】DVM和ART。

qq_31481093的博客 781

【AI智能体】Skills 智能体驱动开发从使用到项目实战详解

Skills 智能体驱动开发从使用到项目实战详解

congge 6611

JVM DVM ART 联系区别

1、JVM是什么? JVM是Java Virtual Machine JVM本质是软件,是计算机硬件的一层软件抽象,屏蔽了底层实现的差异 Java编译的程序会生成JVM字节码,字节码需要通过JVM被翻译成机器指令 JVM本质作用是将平台无关的.class内的字节码翻译成平台相关的机器码-实现跨平台 2、Dalvik虚拟机(DVM)是什么 是android中使用的虚拟机。所有的Android程序...

weixin_41205419的博客 866

反激电源及其变压器计算与绕制

本内容以一个宽电压输入 24V3A 输出的 72W 反激电源为例来计算电路的各个参数和器件选型,包含反激变压器的计算与绕制方法。

让你爱上电路设计 1937

基于OpenClaw的AI数字员工协同团队架构设计

随着大型语言模型从被动的文本生成器向具备自主规划与执行能力的智能代理演进,企业级人工智能的应用范式正在发生根本性转变。在这一演进过程中,OpenClaw(前身为Clawdbot及Moltbot)作为一种开源的、本地优先的个人AI智能体网关基础设施,凭借其对持续内存、多通道接入以及沙盒化工具执行的原生支持,迅速成为构建“始终在线”数字劳动力的核心框架 1。然而,将单体个人助理扩展为能够处理复杂业务逻辑、支持跨组织协同并严格保障数据隔离的多智能体(Multi-Agent)团队,绝非简单的API拼接。系统架构必须

形上得其象,形下合于数;阴阳自济,玄理自明。 1048

Android性能调优之需要掌握Dalvik和ART的知识

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。由于篇幅有限,这里以图片的形式给大家展示一小部分。

xcbyaya的博客 900

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Android内存优化:DVM和ART原理初探

前言要学习Android的内存优化,首先要了解Java虚拟机,此前我用了多篇文章来介绍Java虚拟机的知识,就是为了这个系列做铺垫。在Android开发中我们接触的是与Java虚拟机类似的Dalvik虚拟机和ART虚拟机,这一篇我们就来了解它们的基本原理。1.Dalvik虚拟机Dalvik虚拟机( Dalvik Virtual Machine ),简称Dalvik VM或者DVM。它是由Dan B...

weixin_42336002的博客 4614

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

浅谈android源码之dalvik

文章目录1. 初识DVM2. 虚拟机的启动过程2.1 前言2.2 init启动Zygote2.3 Zygote启动DVM2.3.1 startVm2.3.2 startReg3. 虚拟机与内存管理3.1 前言3.2 启动GC的时机3.3 标记阶段3.4 清除阶段4. 虚拟机与进程管理4.2 客户端请求4.3 处理客户端请求5. 虚拟机解析dex过程5.1 前言5.2 调用OptMain.cpp过...

生于忧患,死于安乐 1542

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

阿里巴巴-说说-Android-虚拟机Dalvik与ART区别在哪里?

现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。

disrm84160的博客 1048

Android 各类虚拟机JVM,DVM,ART区别

Android开发中我们接触的是与Java虚拟机类似的Dalvik虚拟机和ART虚拟机,下面梳理一下三者区别和原理: 一,Dalvik虚拟机 Dalvik虚拟机( Dalvik Virtual Machine ),简称Dalvik VM或者DVM。Dalvik 发音有道词典并没有收录。说说来历,它是由Dan Bornstein编写的,名字源于他的祖先居住过的名为Dalvik的小渔村。DVM

iblade的博客 7773

2024年Android性能调优之需要掌握Dalvik和ART的知识,面试加分句

现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。

2401_84544914的博客 935
上一篇: 通过dexdump来学习DEX文件格式
下一篇: G1 算法论文
漂流的代码
博客等级 码龄23年 188粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值