前言

在作者的上一篇文章《Android R常见GC类型与问题案例》中,对Demo应用的Heap堆结构与Space类型及相对应内存分配算法做了简要的探究,同时对Android R机器运行中常见的GC类型和具体卡顿案例进行了细致的梳理,对Android系统和应用开发人员了解学习ART GC运行策略和优化具体GC类问题具有一定的借鉴参考意义。

承接上一篇文章,本文将对JVM垃圾回收和CC回收器简单介绍,因文章篇幅原因,对CC回收器的细节本文不过多展开,敬请期待作者下一篇对CC回收器具体实现分析的文章,本文重点介绍GC的任务和工程化的挑战,以及CC回收器的特点和优势。

术语

为了后面叙述方便,首先明确以下名词的含义:

u Concurrent: 并发,指回收线程和应用线程可同时运行

u Parallel: 并行,指多个回收线程同时进行垃圾回收工作

u Collector: 进行垃圾回收的线程

u Mutator: 修改器线程,指任何可以修改Heap的线程,一般指应用业务线程

u RootSet: 根集对象集合(全局类、线程上下文类)

u RememberSet: 记忆集,一种数据结构记录堆内跨代、跨区引用

u CardTable: 记忆集的一种实现,一块连续内存地址段对应一个Card,当内存段内的对象存在跨区、跨代引用,标记为dirty_card。

u Tracing: 可达性分析的追踪过程,从根对象标记引用链上的所有对象

u PauseTime: GC时挂起Mutator线程的时间,CC只有一次Pause,在FlipThreadRoots中

u CMS: Concurrent Mark Sweep--并发标记清除回收器

u CC: Concurrent Copying--并发复制回收器

u Partial GC: 部分回收,CC中指回收所有region和其关联的space

u Sticky\Young\Minor GC: 新生代回收,CC中指只回收新生代region

u Full GC: 全堆回收

u   起始、原始快照:追踪过程中,记录被删除的引用,将被删除的引用指向的对象加入标记栈,最后再追踪下此对象(并发阶段死亡的对象也会标记为存活,即漂浮垃圾,下次GC再回收)-G1、CC采用

u   增量更新:追踪过程中,记录新增引用,将引用被修改过的对象加入到标记栈,最终再追踪下此对象,并发阶段死亡对象能被回收,漂浮垃圾少,但需要一个额外的STW最终标记-CMS采用

一、GC的难题(挑战)

在李晓峰的《虚拟机设计与实现:以JVM为例》一书中,提出合格的回收器必须做到的三个方面:

u  正确性:GC不会丢失任何存活对象。任何需要的对象都不能被当成垃圾对象被回收,否则可能出现逻辑错误和进程崩溃。

u  进步性:GC不会保留任何垃圾对象太长时间。即垃圾对象最终都将被清除,短暂地保留一些漂浮垃圾不是问题。

u  可结束:确保Tracing阶段可结束。Tracing阶段的遍历或递归不会出现死循环。

在GC算法的业界研究和工程化应用中,重点是如何做到:

u  更小的PauseTime,对应的提出并发回收、并行回收和分代回收

u  更高的吞吐量,吞吐量=回收的内存/回收耗时

u  更低的内存分配耗时,常见分配算法BumpPointer\Dlmalloc\Rosmalloc\FreeList

u  更少的内存碎片(高效内存整理),如后台进程进行内存整理,对拆分为多个region管理等

u  更高的内存利用率,如可达分析与引用计数混合实现,精准的垃圾回收区域策略等

u  更小的GC数据内存占用开销,如CardTable\MarkBitmap的实现

u  漂浮垃圾回收策略和对象消失的处理手段,对应增量更新、起始快照的实现

二、GC的任务

自动回收进程运行过程中不再需要的垃圾对象(也称孤立对象)。在这个任务中有几个重点:

l   垃圾对象的判定

l   垃圾回收实现方式

l   降低GC对业务的影响(对应上一条:GC的难题)

1)垃圾对象的判定

垃圾对象也称为死亡或孤立对象,相反的则是存活或活跃对象。要寻找垃圾对象,首先的前提:所有对象只有两种状态,不是存活就是死亡。所有对象的合集为存活对象合集和垃圾对象合集组成,没有第三种集合。

主流的有两种算法来判定垃圾对象:

引用关系 图示

A) 引用计数法(RC:Reference counting)

引用计数法的思想是确定哪些对象是死的,在对象从存活到死亡的过程中回收内存,因对象死亡时间不定,所以引用计数类GC具有时刻回收、内存利用率高的特点。

如上图示,引用计数法类的GC会记录每个对象的引用次数,当引用次数为0时,代表此对象没有被任何对象引用,即认为是垃圾对象。引用计数类GC有以下优势:

u  实现简单

u  引用减少至0时,实时回收内存,内存利用率高

u  回收操作可并发运行,无需暂停应用线程

除了优势,在实现运用上也有以下主要弊端:

u  对象环状循环引用(见上图右侧),此类型的对象无法被回收(开源社区有一些复杂方案能解决此场景)

u  Space overhead: 每个对象需要格外空间存储引用次数

u  Speed overhead: 每次引用修改需要增加指令,且多线程情况下需要加锁保证原子操作

因上诉弊端,尤其是环状引用和overhead方面,主流工程化应用的GC都没有选择引用计数法,而是选择可达性分析法。

B) 可达性分析法

可达性分析法的思想是找出哪些对象是活的,活的对象确定后,其他的都是死亡对象(垃圾对象),通过一次批处理完整的清除垃圾对象,回收垃圾对象占用的内存。因此在一次GC的垃圾对象回收前,可以认为所有对象都是存活对象。

如上图示,左边已确定的根集对象出发直接引用和间接引用到的对象都是存活对象,因为根集中的根对象是系统运行和业务逻辑必须的对象,被根对象直接或间接引用的对象当然需要保留。

确认到所有从根集对象直接和间接的对象合集(引用关系图\对象邻接图)后,其他与根对象无关联的对象则被判定为垃圾对象,可以直接进行回收。

可达性分析类GC有以下优势:

u  不存在循环引用问题

u  不存在引用计数法的空间和运行时开销

当然,可达性分析法也有弊端:

u  内存利用率低(在GC回收内存前,垃圾对象占用的内存无法复用)

u  设计复杂,若需要支持并发回收需要额外数据结构支撑

u  PauseTime: 根集枚举与引用Tracing过程一般需要暂停应用线程至少一次,大部分回收器要暂停2-3次

虽然引用计数法和可达性分析法的思想和实现完全不同,但两者其实是互补关系,在业界不乏一些优秀的GC回收器实现整合了两个算法的优点,来提高内存利用率和减小暂停时间。

*这篇文章分析的CC回收器使用的是可达性分析法。

2)垃圾回收的实现方式

在周志明的《深入理解Java虚拟机》一书中,展示了下面几种经典垃圾回收算法:

u 分代收集

建立在以下三个假说之上

弱分代假说:绝大多数对象都是朝生夕死

强分代假说:熬过越多次GC过程的对象越难以死亡

跨代引用假说:跨代引用相对于同代引用来说仅占极少数

因此,商业虚拟机中一般都会将Java Heap堆划分成不同的区域,按照年龄大小将对象聚集在一起,匹配有差异化的回收策略,以提高GC吞吐量和提高内存利用率。

u 标记-清除算法

特点:实现简单,GC耗时短,但会产生大量内存碎片

u  标记-复制算法

特点:解决内存碎片问题,有对象复制的开销,且牺牲一半可用内存

u  标记-整理算法

特点:不存在内存碎片和内存浪费,对象复制与引用更新开销巨大,整理阶段一般需要挂起应用线程

a. ART中各回收器的算法体现

在Android 7(N)及以前的几个Android版本中,应用在前台时使用CMS(并发标记清除)回收器,在前台时重点关注内存回收速度。应用在后台时使用HSC(同构空间压缩)回收器,实质是标记-整理算法的一种实现。

在Android 8(O)之后,应用在前后台都是用CC(并发复制)回收器,应用在前台也能整理内存,减少内存碎片化。应用在后台时,可以最大程度地进行内存整理。在Android 10(Q)版本重新引入了分代回收,每个的Region可能是老年代或者新生代。

Android各版本回收器性能对比(数据源自Google I/O 19)

表中对比发现,CC回收器在内存分配速度、内存整理等方面都有全面的提升,本文后续章节将探究CC回收器的实现原理和策略。

3)降低GC对业务的影响

基于可达性分析实现的GC无法避免的是PauseTime(STW:stop the world),主流的回收器都在致力于减少PauseTime。不支持并发的回收器进行垃圾回收时,需要先暂停进程的所有线程,等待GC线程进行根集遍历(Root visit)、Tracing标记和回收后,进程才能恢复运行。如此长的PauseTime对于用户体验是非常致命的。

为了优化PauseTime,商用回收器基本都采用并发(Concurrent)实现,比如CMS\G1\CC等。

并发机制可以极大的减少PauseTime,一般只需要在枚举线程上下文的根对象才需要暂停线程,比如ART虚拟机的CC回收器只在枚举线程根对象时暂停一次。

并发也带来了实现逻辑的复杂化,需要增加多个关键数据和机制来维护并发期间的数据准确。主要是解决以下的难题:

u  并发期间引用更新的处理,Collector Tracing过程中Mutator持续修改引用

u  存在跨代引用和跨区引用,并持续被Mutator修改

在CC回收器实现中,采用并发机制,极大地减少了PauseTime,依据google官方数据,PauseTime平均为0.4ms,且不随堆大小而变化,只与Roots的数量有关。

CC回收器GC过程 图示

三、CC的实现原理与特点

CC回收器并无独创性的理论突破,借鉴的是开源社区成熟的GC理论和应用成果,结合Android设备和堆内存特点进行的实现应用。主要优化内存分配速度与内存碎片,核心理论突破是将整个堆分为多个同等大小的内存段,化整为零再分而治之。

先回顾下采用CC回收器的app堆结构图。

Demo app堆结构 图示

1)回收流程概述

u    InitializePhase,初始化统计量,设置标记等。

u    MarkingPhase,遍历所有Root根集,将根集对象直接引用对象压入mark_stack,并发标记存活对象记录在mark_bitmap中(此阶段只用于确定回收区域,StickyGC和ExplicitGC不需要)。

u    FlipThreadRoots,依据上一阶段的标记信息,确定回收区域(倾向于回收垃圾对象多的region),回收区域设置为from_space。

u    CopyingPhase,处理跨代、跨区引用的dirty_card,将from_space中的存活对象拷贝至to_space,更新引用等。

u    ReclaimPhase,释放from_space的空间,重置为to_space。

2)PartialGC与StickyGC流程图示

u    Sticky GC

u    Partial GC

3)涉及算法

u  内存分配

整个堆划分为多个同样大小的Region(256KB),Region内分配内存使用最高效的BumpPointer指针碰撞算法,缺点是无法单独释放某个对象的内存(漂浮垃圾无法避免),只能整个释放Region占用的空间。

u  回收算法

全局层面为标记-整理算法,两个Region间为标记-复制算法。有效地减少内存碎片。

u  跨区引用

写屏障支持,用CardTable实现RememberSet,记录跨代/特殊Space与RegionSpace间的引用。

u 并发

由CardTable,转发指针,起始快照,目标空间不变(只访问to_space对象)等特性支持。

4)参考设计

借鉴G1/Shenandoah回收器思想,化整为零,分代收集。

5)基础原理

u  写屏障(两种类型:写前\写后)

修改对象的引用型成员变量时执行一段特殊代码。CC中引用修改会将对象关联的Card标记为Dirty,用于记录自上次GC之后和GC并发期间的引用变化。

u  读屏障

读取对象的引用型成员变量访问目标对象时执行一段特殊代码。CC中通过目标对象的monitor_成员变量关联的LockWord对象判断目标对象是否被拷贝。未拷贝则Mutator将目标对象进行拷贝并修改原对象的LockWord使其指向新对象,且将对象的引用修正为新的目标对象。

u  转发指针

被拷贝后的from_space原对象LockWord配置转发指针,之后所有访问原对象都会自动指向to_space新对象。

6)优势

u  指针碰撞分配,贡献最高的内存分配速度。

u  前后台内存整理,更少的内存碎片,可控的漂浮垃圾数量。

u  更灵活的内存管理单位,提升内存利用率,相比CMS降低平均32%的堆占用(google官方数据)。

u  更精准的分代垃圾回收策略,提升吞吐量(回收效率),减少PauseTime。

7)不足

u  预留两倍堆空间的内存地址(32位应用影响明显,增加虚拟内存地址不足OOM概率)。

u 并发复制过程耗时随堆大小、对象数递增,耗时较长。

u Mutator线程参与对象拷贝和Tracing,存在对象移动风暴。

u 读写互斥锁与锁堆特殊场景阻塞应用线程。

u 无法单独释放某个对象的内存,并发过程中新增对象默认为存活,存在一定的漂浮垃圾。

四、总结

本文重点着力于阐述GC并发所面对的问题和对应的优化手段,结合Android ART虚拟机中CC回收器的实现原理和回收流程,简短的概述文章,希望读者看完能增加对GC的认识。GC理论发展几十年,依靠CPU算力提升和以空间换时间等经典思想,逐步地将GC对应用线程的影响一步步降低。有个说法JAVA开发者无需过多关注GC,但系统开发者和应用架构师掌握虚拟机内存管理机制是很有必要的,对于指导业务优化和实现高级功能都有助益。

作者下一篇文章将CC回收器具体代码进一步剖析,Stay Tuned!

参考资料

1.《虚拟机设计与实现:以JVM为例》--李晓峰

2.《深入理解Java虚拟机》--周志明

3.Android ART并行拷贝垃圾回收--王允臣

4.Android GC简史--掘金:二手认知

5.Understanding Android Runtime (ART) for faster apps (GoogleI/O'19)--youtube

6.ConcurrentCopying Google I/O '17--youtube

7.Model Checking Copy Phases of Concurrent Copying Garbage Collection withVarious Memory Models - YouTube

8.Java 垃圾回收权威指北--刘家财

9.深入浅出垃圾回收--刘家财

10.JVM 三色标记 增量更新 原始快照

长按关注内核工匠微信Linux 内核黑科技 | 技术文章 | 精选教程

JVM 垃圾回收算法与ART CC回收器实现概述相关推荐

  1. jvm垃圾回收算法和垃圾回收器

    垃圾回收算法 jvm垃圾回收算法包括复制算法.标记清楚算法和标记整理算法,它们都基于分代收集理论.所谓分代收集理论,可以理解为jvm根据对象的生命年龄将他们分在不同的内存模块,也就是熟知的新生代和老年 ...

  2. JVM垃圾回收算法及垃圾回收器

    目录 一.垃圾回收GC (Garbage Collection) 1.1 垃圾回收介绍: 1.2  如何判断对象是否存活(被使用) 1.2.1 引用计数算法(已经废弃) 1.2.2 可达性分析 二.垃 ...

  3. java jvm垃圾回收算法_深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 喜欢的话麻烦点下Star哈 文章将同步到我的个人博客: www.how ...

  4. JVM垃圾回收算法与原理详解

    垃圾回收 参考文档 GC参考手册-Java版 理解Java的强引用.软引用.弱引用和虚引用 JVM系列(五) - JVM垃圾回收算法 如何判断对象可以回收 引用计数法 参考文章 Java JVM的引用 ...

  5. jvm垃圾回收算法详解

    前言 相比C语言,JVM虚拟机一个优势体现在对对象的垃圾回收上,JVM有一套完整的垃圾回收算法,可以对程序运行时产生的垃圾对象进行及时的回收,以便释放JVM相应区域的内存空间,确保程序稳定高效的运行, ...

  6. JVM 垃圾回收算法 -可达性分析算法!!!高频面试!!!

    前言:学习JVM,那么不可避免的要去了解JVM相关的垃圾回收算法,本文只是讲了讲了可达性分析算法,至于标记-清除.标记-复制,标记-整理,分代收集等等算法,会在近两天的文章中陆续更新出来. 很喜欢一句 ...

  7. JVM 垃圾回收算法及回收器详解

    本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Python是 ...

  8. 胡说八道JVM—垃圾回收算法和垃圾回收器

    垃圾回收算法 引用计数器法(Reference Counting) 可达性分析 标记清除算法(Mark-Sweep) 这个算法的原理很简单,但是它却是其他算法的基础,后续的其他算法否是在这个算法的基础 ...

  9. JVM垃圾回收算法和回收器

    一.垃圾回收算法: 1.引用计数法 引用计数法就是对于一个对象A,只要有任何一个对象引用了A,则A的计数器就加1,当引用失效时就减1.只要对象A的计数器数值为0,将会被回收. 注意:无法处理循环引用的 ...

最新文章

  1. 字符串分隔 -连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组; •长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。...
  2. exchange2013警告The maximum number of concurrent connections has exceeded a limit
  3. 蓝懿教育九月八日记录
  4. 围观了张一鸣近10年的微博,我整理了这20多条经验之谈
  5. 积木报表对比帆软报表有什么区别?
  6. linux安装oracle11g步骤_图解 Debian 10(Buster)安装步骤 | Linux 中国
  7. in作为介词的用法_思维导图:为孩子收藏——常出错的英语用法集锦
  8. 优课计算机考试,新生入学安全教育考试之优课操作流程
  9. MLA 格式引用怎么做好分析?
  10. 【ARMv8基础篇】NIC-400控制器简介
  11. Python第一周学习总结
  12. 微信开发者工具下载及调试
  13. 计算机研究生就业方向之去央企(国企)信息化部门
  14. 什么是自动气象站 校园气象站
  15. 解决win10系统下Elasticsearch闪退问题
  16. 上海韬源信息技术有限公司怎么样
  17. bit.ly短网址API
  18. 手把手教你学Go(二)——Hello world
  19. 《黑马程序员》 初识黑马
  20. python文献检索_那个发了好几篇SCI的师姐,教你如何搞定文献检索和科研图片!...

热门文章

  1. 华为鸿蒙遭狙击,一周概念复盘简讯:狙击华为鸿蒙概念股(来收藏)
  2. Python爬取《你好李焕英》豆瓣短评并基于SnowNLP做情感分析
  3. NB-IoT卡eSIM卡(5*6毫米)贴片卡引脚定义及尺寸
  4. 【论文速递】BLIP:Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and G
  5. 自动控制原理大作业——已知某位置测控装置如图所示
  6. arduino 多个超声波模块HC-SR04 Newping.h库的使用——摆脱万恶的阻塞等待
  7. 掌握及手把手实现哈希表
  8. 【渝粤教育】国家开放大学2018年秋季 0686-21T广告创意与表现(一) 参考试题
  9. iOS 镜头变焦,推近或者拉远焦距--ZOOM
  10. UnityEditor查找引用和批量替换资源工具