ART虚拟机GC系统优化研究

2017-03-06 21:47张加帅
电子技术与软件工程 2016年23期

摘 要

Android系统中虚拟机通过提供垃圾回收机制(Garbage Collection,GC)实现自动内存管理,但是这一机制在简化应用开发人员内存管理负担、避免内存泄漏问题的同时也给系统带来了内存驻留、响应延迟、并发阻碍等负面影响。Android 5.0以后,Google用ART虚拟机替换掉Dalvik虚拟机,为了更好地利用ART虚拟机自动内存管理的特性,应用开发人员有必要深入理解ART虚拟机垃圾回收机制的实现。本文从GC算法和堆分配过程研究了ART虚拟机GC系统,本文提出的动态设置堆利用率以减少Full GC的方式优化GC系统性能,最后测试多组应用程序对比优化前后在GC次数、暂停时间和GC总时间等方面的差异。整体平均暂停时间相较于优化前降低20%以上,而平均GC总次数减少5次以上,证明本文实验条件下动态设置堆利用率的方式优化Full GC对GC系统性能的提高是有效的。

【关键词】ART虚拟机 GC系统 堆利用率

ART虚拟机GC子系统对Android系统和上层应用程序的稳定性和流畅性具有非常大的影响。本文提出的动态设置堆利用率以减少Full GC的方式优化GC系统性能,最后测试多组应用程序对比优化前后在GC次数、暂停时间和GC总时间等方面的差异。

1 垃圾回收机制

1.1 堆内存的对象分配和释放

ART虚拟机实现的垃圾回收机制是一种自动内存管理机制,对象分配和收集都在堆上进行。传统的C/C++是没有GC的,初始化对象然后分配内存空间需要手动地malloc/new,当需要销毁对象时候需要手动地free/delete。内存管理使用这种模式导致至少两个明显的问题:指针悬挂和内存泄漏。而采用了垃圾回收机制的JAVA中,软件开发人员只需考虑申请内存,垃圾回收机制可以根据一定的规则判断出内存空间中的“垃圾”,自动释放“垃圾”占用的内存。不过付出的代价是GC耗费额外的系统资源,并会暂停用户进程,严重时候明显影响用户体验,例如系统卡顿、动画掉帧等。与此同时潜在一个危机是,一旦出现内存泄漏或溢出的问题,如果开发人员不了解虚拟机内存管理机制,那么解决这类问题就变得棘手。

1.2 ART虚拟机的GC算法

Java垃圾回收机制采用的主流GC算法包括:引用计数算法(Reference counting)、标记-清除算法(Mark-Sweep GC)、复制算法(Copying GC)、标记-合并算法(Mark-Compact GC),后面三种算法属于追踪式算法,如表1所示。

ART虚拟机默认的两种垃圾回收技术,一种是Mark-Sweep,另一种是Semi-Space GC。应用在前台时候设置GC采用Mark-Sweep算法,应用运行在后台时候设置GC采用Semi-Space算法。本文主要关注运行在前台的Mark-Sweep算法的执行性能。

Mark-Sweep:它的大致思想是,将所有的对象在内存中的位置记录在位图A中。然后,从所有对象的根出发,扫描根对象的所有引用,扫描根对象的所有引用的引用,一层层逐级扫描,直到叶子节点的对象。在这个逐级扫描的过程中,将涉及到的对象的位置都记录在位图B中。扫描结束后,对比两张位图A和B:所有A中置位的位置,却没有B中被置位,被视为垃圾。并根据位图中的索引检索得到对象,然后释放该对象占用的内存。

Semi-Space GC: 它的特点是需要有两个space空间,其中一个备用。另一个被系统拿来分配对象。在做垃圾扫描的时候,将所有在空间A中被有效引用的对象移动到空间B中。那么在空间A中剩余的对象就是垃圾了,直接释放垃圾对象所在的整个空间就可以了。这个回收速度很快。

1.3 ART虚拟机的堆结构

堆(Heap)可以是由字构成的连续数组,也可以组织成不连续、由字构成的块的集合,与数据结构中堆的概念不同,内存管理中的堆用来存放各种用户或系统申请的数据对象,在内存中占据非常大的空间,因此也常将堆空间用内存来代替。

如图1所示,ART虚拟机中Mark-Sweep GC堆结构分6个区域,分别为Image Space、Zygote Space、Non-Moving Space、Large Object Space、Main Space以及Backup Space。Image Space存放预加载的系统类对象,Zygote Space在Zygote进程和应用程序进程之间共享,存放Zygote进程所需的预加载的类、资源和对象,它是Zygote进程在fork第一个应用程序时候从Non-Moving Space划分出来的。Non-Moving Space是用来存放不需要移动的对象的,有些对象例如类对象、类方法和类成员变量对象一经加载后,就基本一直存在,频繁的移动此类对象代价较大且没必要。Large Object Space是用来分配大对象的,當分配的原子类型数组的大小大于3个内存页时,就把此对象分配在Large Object Space中。其他的对象就分配在Main Space中。

2 ART虚拟机GC执行过程分析

2.1 GC执行过程

ART运行时与Dalvik虚拟机一样,也使用了Mark-Sweep算法进行垃圾回收。但是在ART运行时里的Mark-Sweep算法更加优化。原来Dalvik虚拟机里的垃圾回收需要暂停两次,标记根集阶段和重新标记根集阶段,两次暂停时间如果很长或很频繁就会对用户进程有很大的负面影响。而在ART虚拟机取消了第一次暂停,原来在第一次暂停所需要做的工作分给了线程自己,在整体上提高了ART运行时的垃圾回收的性能。

如图2所示,GC执行过程分为以下几个阶段:

2.1.1 标记阶段

首先标记根集对象,包括当前线程栈中的对象、当前进程所有已经加载的类及类中的静态引用等。然后从根集对象开始递归标记出所有可达对象。

2.1.2 暂停阶段

标记阶段在标记根集的时候没有暂停其他线程,所以在标记的过程中对象的引用关系可能会发生变化,如果不处理这种变化就会导致对象的错标或漏标,后面清扫垃圾时就有可能把存活对象也回收了,导致程序的非正常运行。所以需要有个暂停阶段,来处理标记阶段中对象的引用关系发生变化的对象。

2.1.3 清除阶段

经过了前面的两个阶段,标记清除算法认为的存活对象都在标记位图(Mark Bitmap)中标记出来了。接下来回收没有被标记的内存块。

2.1.4 终结阶段

在回收垃圾后有个收尾工作,会对经过垃圾回收后的堆根据需求进行裁剪,也会对堆的预留空闲内存进行重新设定。

2.2 堆内存分配过程

对于标记清除算法,ART有三种GC策略:分代垃圾回收(Sticky GC)、局部垃圾回收(Partial GC)以及全局垃圾回收(Full GC)。分代垃圾回收只回收上一次GC到本次GC之间申请的内存。局部垃圾回收不回收Image Space和Zygote Space空间的内存。全局垃圾回收回收除了Image Space之外的空间的垃圾。GC 暂停时间:Sticky GC < Partial GC < Full GC,回收垃圾的效率则反之。为了在垃圾回收性能和垃圾回收效率之间追求平衡,ART采用了一种渐进式的分配策略。而影响GC暂停时间和总时间最大的Full GC是本文优化目标。

GC时候,首先会进行一次轻量级的GC, GC完成后尝试分配。如果分配失败,则选取下一个GC策略,再进行一次轻量级GC。每次GC完成后都尝试分配,直到三种GC策略都被轮询了一遍还是不能完成分配,则进入下一阶段。第二阶段允许堆进行增长的情况下进行对象的分配,如果还是分配失败,则会进行一次允许回收软引用的GC。如果还是分配失败就进入第三阶段如果对象是不可移动对象,则ART会把它分配在Non-Moving Space。如果对象是可移动的对象,那么就进行一次同构空间压缩,压缩后也增加了分配成功的可能性。

3 性能调优

3.1 与GC相关堆属性值

如图3所示,在build.prop中一般会设置如下几个与Android ART虚拟机垃圾回收相关的属性值:前三个值是控制Java堆的总大小的,包括堆的起始大小、堆的增长上限等,与手机的硬件配置相关。堆利率、堆最小空闲内存和堆最大空闲内存三个变量对垃圾回收的某些性能有影响。GC触发后,垃圾回收器回收了应用不再使用的垃圾对象,这样应用的空闲内存就可能很大或者由于回收垃圾不够多导致空闲内存还是很小。空闲内存很大,Android系统内存利用率就低,当然不会把这么大一块内存都给应用程序的,出于这种考虑系统会根据应用预先设定的堆利用率(HeapTargetUtilization)、最大和最小空闲内存数(heapmaxfree、heapminfree)等参数来调整此空闲内存的大小;如果此空闲内存很小,那么势必此空闲内存将很快分配光,下次GC会来的很快,所以遇到这种情况,ART会扩大此空闲内存的大小堆利用率(HeapTargetUtilization)、最大空闲内存(heapmaxfree)和最小空闲内存(heapminfree)在代码里的变量名为:utilization,max_free_,min_free_。

如图4所示,堆利用率(utilization)算可以出理论上所需的空闲内存,堆利用率按照谷歌的推荐一般设为0.75。考虑另外两个限制值:最小空闲内存(min_free_)和最大空闲内存(max_free_)。也就是需要把预留空闲内存控制在两倍的最小空闲内存和两倍的最大空闲内存之间。这样获得的target_size才是堆的最终大小。

3.2 使用堆目标利用率减少Full GC频率

如2.2节描述,当对象分配请求得到满足时候,用戶进程就会继续执行,不过如果对象分配失败,则说明当前的堆大小是不能满足进程需求的。事实上对于现有应用运行用户体验来看,ART虚拟机仍有很多可以优化空间,比如一些大型游戏或消耗资源的应用中,Full GC频率仍然较高,使得暂停时间和GC频率的增加。为了获得更有效率的内存管理,需要在应用运行过程中,人为干涉GC处理,本文通过设计自适应的对增长机制来满足应用运行状态的动态改变。使用dalvik.system.VMRuntime类提供的接口setTargetHeapUtilization方法增强应用程序堆内存的处理效率。比如在应用程序onCreate的时候就调用下面的一个用于设置利用率大小的函数VMRuntime.getRuntime().setTargetHeapUtilization即可,本文实现堆利用率动态调整实现流程如图5所示。

当完成申请的时候系统会继续执行,根据堆利用率设置新的理想堆大小,减少完全GC的方法就是在当系统剩余的内存不足以完成本次申请操作时,将本次申请的对象转移到缓冲堆,此时根据堆目标利用率设置堆的理想大小,这样由于内存不足产生的完全GC就可以避免,这样就避免的长时间的暂停,而且堆此时已经生成为理想大小,不会使得后面堆空间不足而循环产生GC动作增加GC频率。

4 实验设计与性能测试

本实验采用的硬件平台是Google公司的Nexus4,软件调试平台是Android Studio。对0xbenchmark 和用户应用优化前后GC次数、GC 的暂停时间进行统计分析。

4.1 基于0xbenchmark性能测试

0xBenchmark中有专门测垃圾回收性能的模块。测试的算法是递归自顶向下和递归自底向上创建完全二叉树,以及创建大的浮点数组,对于内存块的创建还分为长生命周期对象和临时对象,长生命周期对象的引用要在测试函数运行完毕时才会丢失,而临时对象在创建完毕后即被丢失,测试的标准是创建所需时间,暂停时间以及GC总时间等。

4.2 基于实际应用运行log信息性能测试

动态设置堆利用率以减少FULL GC优化手段带来最直接的影响就是减少垃圾回收暂停时间,虽然0xbenchmark有Android的VM垃圾回收性能测试,但是不能符合正常情况下应用对象大小并不固定的事实。所以除了使用0xbenchmark评估性能表现外,本文选取了几个常见的应用作为测试对象。分析这些应用运行过程的log信息,主要是暂停时间,来评估优化效果。

4.3 性能测试结果

比较表2和表3的情况,原生垃圾回收信息和优化后的垃圾回收信息,FullGC的暂停时间是占用平均暂停时间很大比例,优化FullGC对于GC平均暂停时间有很大贡献,另一方面优化后GC总次数降低了,总次数的减少是因为本文增加一个设置堆利用率的过程,满足新对象对堆空间的需求,以Full GC需求减少进而减少了总GC次数。从应用的整体来看,平均暂停时间相较于优化前降低20%以上,而平均GC总次数减少5次以上。本文实验条件下动态设置堆利用率的方式优化Full GC对GC系统性能的提高是有效果的。

5 结束语

垃圾回收机制实现自动内存管理的代价是额外消耗系统资源、阻塞用户进程的执行等。本文从GC算法和堆分配过程研究了ART虚拟机GC系统运作机制,提出的动态设置堆利用率以减少Full GC的方式优化GC系统性能。设计实验测试了多组应用程序,结果显示整体平均暂停时间相较于优化前降低20%以上,平均GC总次数减少5次以上。

参考文献

[1]Andrei Frumusanu. A Closer Look at Android RunTime (ART) in Android L [EB/OL].http://www.anandtech.com/show/8231/a-closer-look-at-android-runtime-art-in-android-l/

[2]付卓.Dalvik内存管理机制分析与优化[D].南京:东南大学集成电路学院(硕士学位论文),2013.

[3]黄洋.Android内存管理中的垃圾收集性能优化[D].南京:东南大学电子学院(硕士学位论文),2014.

[4]夏迪迪.Dalvik虛拟机垃圾回收算法的分析与性能优化[D].无锡:东南大学集成电路学院(硕士学位论文),2015.

[5]王浩.面向用户体验的GC策略分析与优化[D].无锡:东南大学集成电路学院(硕士学位论文),2015.

[6]老罗.ART运行时Mark-Sweep GC简要介绍和学习计划 [EB/OL].http://blog.csdn.net/luoshengyang/article/details/44513977,2014.

[7]Android开发工具文档[EB/OL].http://developer.android.com/tools/index.html,2011.

[8]0xbench [EB/OL].https://code.google.com/p/0xbench/,2011.

作者简介

张加帅(1990-),男,硕士研究生学历。研究方向为程序语言自动内存管理。

作者单位

东南大学微电子学院 江苏省无锡市 214135