ART虚拟机CMC GC算法核心实现介绍

描述

一、当前GC算法及存在的问题

当前Android(O-T)一直使用的是Concurrent Copy GC算法(CC),该算法通过FromSpace和ToSpace两个空间实现了非常巧妙的Copy机制,算是空间换时间(或者说复杂度),所以在GC的过程中,两个Space会同时被利用,极端情况会占用内存会double。

此外,为了保证GC过程中,访问对象的一致性,还引入的ReadBarrier,ReadBarrier会在访问对象的时候判断对象是否经过了拷贝,如果经过了拷贝需要返回拷贝后在ToSpace中的地址,反之,仍然返回拷贝前的地址。ReadBarrier会对每次对象访问,都插入一次是否访问的判断,即使当前没有在GC也会发生一次判断。这就导致了执行了多余的指令,且在编译结果二进制文件体积的增长。

总结一下,CC主要存在两个问题:

GC过程物理内存徒增又陡降

为保证一致性引入了ReadBarrier

二、新的Concurrent Mark Compact GC算法

Android马上要使能的新算法是Concurrent Mark Compact,简称CMC算法。CMC旨在解决上面介绍的CC算法存在的两个问题,其中最核心的是利用Linux的UFFD特性,并且更新了Allocator算法。

1、UFFD特性介绍

UFFD全称User Fault FD,其核心机制就是,先注册并监控虚拟内存范围的访问,当该范围的内存访问出现异常时,比如SIGBUS错误时,可以把对这块内存页的处理交接给用户空间。

此外,在没有发生SIGBUS错误时,也可以利用UFFD的ioctl特性进行目标内存页的处理。

2、BumpPointerSpace分配器

CMC对应的分配器是BumpPointerSpace,相对RegionSpace其结构更简单,也更利于全局的内存拷贝压缩。

3、GC拷贝压缩的准备工作

在CMC对象初始化的时候,也会初始化跟内存拷贝压缩的相关的数据结构。

在对Heap中对象进行全面遍历标记的时候,会根据对象是否存活,来记录和累加计算其压缩后的地址,还有被压缩后的各个内存页的首个对象,主要的数据结构有:

创建info_map,其中包含:

chunk_info_vec:记录存活对象的大小,所有该容器元素的累加和就是所有存活对象内存的大小。

first_objs_moving_space:会在PrepareForCompaction阶段,记录被压缩后某页的首个对象的源地址。

pre_compact_offset_moving_space:会记录页面被压缩后的地址偏移。

创建compaction_buffers_map:会被用于压缩过程中的页面级别的压缩,作为中转缓存,最后再通过UFFD特性被拷贝到目标地址页面。

① MarkingPhase阶段

该阶段会遍历所有的对象,并在处理存活对象的时候,调用UpdateLivenessInfo函数,该函数主要做了下面两个事情:

SetLiveWords记录存活地址和大小

chunk_info_vec批量记录活跃内存大小

② PrepareForCompaction阶段

该阶段比较重要的是InitMovingSpaceFirstObjects函数。该函数会找出每个需要移动的内存页的第一个对象,并找出这些对象应该被复制到的目标页偏移量

查找第一个live word,使用live_words_bitmap来找出第一个活动字的偏移量,然后计算出第一个活动对象的地址,并将第一个对象的地址和偏移量存储到first_objs_moving_space和pre_compact_offset_moving_space向量中。2.函数进入一个循环,找出每个需要移动的页的第一个对象和偏移量。并将它们存储到first_objs_moving_space和pre_compact_offset_moving_space向量中。

4、单页内存拷贝压缩

单页的拷贝主要是通过调用DoPageCompactionWithStateChange函数实现的,主要包含两个步骤:

CompactPage:拷贝到中转页

CopyIoctl:通过UFFD拷贝中转页到目标页地址

其中,CompactPage压缩的输入和处理过程如下:

输入

first_objs_moving_space[idx]压缩页的第一个对象pre_compact_offset_moving_space[idx]压缩页offset

处理过程

基于live_words_bitmap_来遍历内存页中的存活对象,逐步将数据复制到目标地址到中转页。

5、并行拷贝压缩

上一小节我们介绍了单页拷贝压缩的过程,全局的拷贝一般是从后往前的拷贝的,但是因为CMC也是并行的,所以GC过程中,非GC线程也就是Mutator线程还是会访问对象的。CC算法是通过ReadBarrier实现这个阶段数据一致性的,CMC是通过UFFD触发的SIGBUS特性实现的,也就是当访问一个对象时候,如果还没有被分配就会导致SIGBUS异常,这时候虚拟机会通过UFFD的SIGBUS异常获取缺页的地址,从而触发对应页的拷贝压缩。

总之,我们可以看到会有两种页拷贝和压缩:

GC线程串行压缩和拷贝

Mutator线程通过SIGBUS异常异步触发

而为保证页同时只会被一个线程处理,虚拟机通过ProcessState原子变量(每个页都有一个)保证异步并行,等所有页都被处理完后,GC过程就结束了。

6、其他内容

BlackAllocation(GC过程中新增的分配,被默认为黑色,不进行回收),通过SlidePage处理。

CompactPage和SlidePage中的跨页对象处理。

压缩后FromSpace页的清理等。

三、CMC算法的优缺点

1、优点

GC过程中RSS峰值降低

消除了ReadBarrier,BinarySize减小,Object访问性能提升

2、缺点

一次GC活跃的对象被拷贝两次

暂时没有实现YoungGC

缺页中断产生的时延可能会导致GC过程的性能退化





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分