前言
Android系统的APP运行需要依赖ART虚拟机(Android Runtime),ART虚拟机的主要作用是给APP的java代码提供运行环境,其中编译、执行、垃圾回收(GC)模块是ART虚拟机的重中之重。GC使得java开发人员能专注于业务实现,而不用担心内存泄漏。
此文章将简要的向大家介绍ART虚拟机中Heap布局、常见GC类型和对应的问题案例。为大家分析优化应用提供一些思路。
本文基于的代码和调试手机系统为Android R(11)版本。
一、GC的相关配置
1. 内存回收器(回收算法)、内存分配器
因为Android R支持读屏障(kUseReadBarrier),在虚拟机创建阶段,内存回收器设置为:
*前台回收器foreground_collector_type = kCollectorTypeCC
*后台回收器background_collector_type = kCollectorTypeCCBackground
##对于CC(ConcurrentCopying)并发复制回收器而言,前后台回收器只用于配置前后台GC时的一些回收器参数,实质回收器只有一种即kCollectorTypeCC。一般当应用退到后台,系统希望应用尽可能释放UI相关的垃圾对象并最大程度的进行内存整理。
##读屏障(kUseReadBarrier)有三种实现方式,涉及到内存回收算法的底层实现逻辑,读者可自行了解,本文不做阐述。
ART虚拟机创建时先确定内存回收器的类型,进而绑定对应的内存分配器,因回收器已设置为CC(ConcurrentCopying)即并发复制回收器,则Heap内存的主要分配区域定为RegionSpace,RegionSpace由一个个256KB的Region组成。对应的RegionSpace内存分配器定为kAllocatorTypeRegion。
从下图代码中可知,并发复制回收若启用分代GC,默认回收策略是Sticky即只回收上次GC后新生成的对象。若未启用分代,默认回收策略是Full即扫描所有space包括zygoteSpace的对象做回收。Android R版已启用分代,则默认策略模式是Sticky。
二、ART虚拟机Heap内存布局
1. 应用Heap布局示例
作者使用Android R版本手机,安装HelloWorld应用查看进程map文件,应用的Heap布局如下图。
此手机Heap参数和应用large_heap配置如下,应用可用堆上限为256MB。
[dalvik.vm.heapgrowthlimit]: [256m]
[dalvik.vm.heapsize]: [512m]
android:largeHeap="false"
2. Space类型
(1)RegionSpace [512M]:
A) 虚拟地址起点为300MB(0x12c00000),可看到图中和ImageSpace之间地址不连续。
B) 虚拟地址范围:为支持堆完整复制,需要额外预留一倍空间。当进程启动后BindApplication阶段调用ClampGrowthLimit()将RegionSpace 虚拟地址空间size设置为两倍的堆上限。
C) 内存分配:绝大多数的对象分配的区域。RegionSpace由256KB大小的Region组成,Region的分配算法是最简单的指针碰撞(BumpPointer)。
当一次分配请求到来,通过For循环遍历可用的Region。当已分配的Region数量已超过总数的一半,不允许再占用新的Region,只能依赖内存回收和整理来释放内存用于分配。
D) 回收策略:当一个Region中存活对象占用大小超过75%时,此Region标记为kRegionTypeUnevacFromSpace,表示无需进行拷贝。否则将此Region中存活对象拷贝至标记成ToSpace的另一个Region中,拷贝完成后回收整个Region,立刻释放256KB内存。
具体的算法实现更加复杂,读者可进一步了解CC算法的实现。
(2) ImageSpace [4M]:
A) Zygote进程创建时,根据boot.art文件ImageHeader结构体中指定的映射地址(0x70e44000),创建ImageSpace,将boot.art内存镜像文件映射到进程的虚拟地址空间。
B) ImageSpace不支持分配对象,也无需回收。
C) ImageSpace创建后同步映射boot.oat文件到ImageSpace地址后面。
D) ImageSpace中映射的.art文件可能有多个,比如boot-core-libart.art/boot-okhttp.art。boot.o