Java 虚拟机垃圾收集器(1)— 经典垃圾收集器

前言

没有完美的垃圾收集器,只有最适合具体应用的垃圾收集器。

1. Serial 收集器

新生代收集器,最基础且历史最悠久的收集器,在 JDK 1.3.1 之前是 HotSpot 的唯一选择。

工作图

图1. Serial 收集器(前)和 Serial Old(后)收集器运行示意图

参数

-XX:SurvivorRatio
Eden 区与 Survivor 区的比例。
-XX:PretenureSizeThreshold
晋升老年代对象大小。

优点

  • 足够简单,额外内存消耗最少
  • 在单核处理器或处理器核心较少时,单线程收集效率最高

缺点

  • 单线程 收集器
  • 进行垃圾收集时,必须暂停其他所有工作线程

使用场景

  • 单核处理器或处理器核心较少
  • 运行在客户端模式
  • 内存资源受限

2. ParNew 收集器

新生代收集器,使用标记 — 复制算法,实质上为 Serial 收集器的多线程并行版本。其控制参数、收集算法、暂停线程、对象分配规则和回收策略等都与 Serial 收集器一致,它们的实现代码也有相当多共用部分。

JDK 7 以前,它是不少运行在服务端模式下的 HotSpot 虚拟机首选的新生代收集器。
JDK 9 以前,只有 Serial 和 ParNew 能与 CMS 收集器配合工作,JDK 9 开始 ParNew 只能和 CMS 收集器绑定使用。

工作图

图2. ParNew(前)和 Serial Old(后)收集器运行示意图

参数

同 Serial 收集器

优点

  • 支持多线程并行收集

缺点

  • 进行垃圾收集时,必须暂停其他所有工作线程

3. Parallel Scavenge 收集器

新生代收集器,使用标记 — 复制算法,和 ParNew 收集器一样支持多线程并行收集,但 Parallel Scavenge 收集器更关注吞吐量,即 处理器用于运行用户代码的时间 / 处理器总消耗时间,此处总消耗时间为 运行用户代码的时间 + 运行垃圾收集时间

停顿时间越短,越适合需要与用户交互或需要保证服务响应质量的程序。
吞吐量越高,处理器资源利用率越高,越快完成程序的运算任务,适合不需要太多交互的后台运算、分析任务。

参数

-XX:MaxGCPauseMilliis
最大垃圾收集停顿时间,大于 0 的毫秒数。值不宜过小,会导致新生代空间缩小,以换取更短停顿时间,进一步导致更频繁的 GC,吞吐量下降。
-XX:GCTimeRatio
吞吐量大小。值为吞吐量的倒数,即垃圾收集时间占总时间的比率。
-XX:+UseAdaptiveSizePolicy
动态设置新生代大小、Eden 与 Survivor 区的比例、晋升老年代对象大小等参数,提供最合适停顿时间或最大吞吐量。

优点

  • 支持多线程并行收集
  • 自适应调节参数
  • 吞吐量相较于 ParNew 收集器更好

缺点

  • 进行垃圾收集时,必须暂停其他所有工作线程

工作图

图3. Parallel Scavenge(前)和 Serial Old(后)收集器运行示意图

4. Serial Old 收集器

老生代收集器,使用标记 — 整理算法,Serial Old 是 Serial 收集器的老年代版本,故也为单线程收集器。

工作图

图4. Serial 收集器(前)和 Serial Old(后)收集器运行示意图

优点

  • 足够简单,额外内存消耗最少
  • 在单核处理器或处理器核心较少时,单线程收集效率最高

缺点

  • 单线程 收集器
  • 进行垃圾收集时,必须暂停其他所有工作线程

使用场景

  • 单核处理器或处理器核心较少
  • 运行在客户端模式
  • JDK 5 及以前版本下,与 Paraellel Scavenge 收集器搭配使用
  • 作为 CMS 收集器失败时的后备预案

5. Parallel Old 收集器

老生代收集器,使用标记 — 整理算法,Parallel Old 是 Parallel Scavenge 收集器的老年代版本,故也为多线程收集器。

出现于 JDK 6,结束了此前 Parallel Scavenge 收集器只能搭配 Serial Old 收集器使用的状态。

工作图

图5. Parallel Scavenge 收集器(前)和 Parallel Old(后)收集器运行示意图

优点

  • 支持多线程并行收集
  • 自适应调节参数
  • 吞吐量相较于 ParNew 收集器更好

缺点

  • 进行垃圾收集时,必须暂停其他所有工作线程

使用场景

  • 注重吞吐量
  • 处理器资源不充足

6. CMS 收集器

老生代收集器,使用标记 — 清除算法,支持多线程并行收集,以最短回收停顿时间为目标的收集器。CMS 收集器在 JDK 5 中发布。

工作图

图6. CMS 收集器运行示意图

工作流程

1. 初始标记

仅标记 GC Roots 能直接关联到的对象,速度较快,但需要暂停用户线程。

2. 并发标记

从 GC Roots 直接关联对象开始遍历整个对象图的过程,耗时较长但不影响用户线程运行。采用 增量更新

3. 重新标记

修正并发标记期间,用户线程运行导致标记产生变动的那一部分对象的标记记录,速度略慢于初始标记,但远快于并发标记。

4. 并发清除

清理删掉标记阶段判断已经死亡的对象,不需要移动存活对象,故可与用户线程并发。

参数

-XX:CMSInitiatingOccupancyFraction
CMS 触发垃圾回收的内存占用比阈值

优点

  • 支持多线程并行收集
  • 停顿时间较短

缺点

  • 对处理器资源非常敏感
    CMS 默认启动的回收线程数为 (处理器核心数量 + 3) / 4。当处理器核心数在四个及以上时,只占用不超过 25% 的运行资源,且核心数越多,占用越低;但核心数不足四个时,回收线程对用户程序影响会变大。
  • 会产生浮动垃圾,有可能出现 并发失败,进而执行 Full GC,导致所有线程暂停
    并发清理阶段,用户线程并发执行,会伴随新的垃圾对象产生,但这部分垃圾出现在标记之后,无法在此次垃圾收集中清理,故留到下一次垃圾收集中清理,此部分垃圾被称为浮动垃圾。
    在并发垃圾收集时,用户线程需要持续进行,故需要预留足内存空间给用户线程使用,JDK 5 为 68%,偏保守;JDK 6 为 92%,又略激进,直接导致面临预留空间不足而导致并发失败,并需要启动 Serial Old 收集器进行老年代垃圾收集,暂停用户线程,造成更久的停顿时间。
  • 空间碎片多
    空间碎片多是标记 — 清除算法的通病,这将导致大对象容易找不到足够的空间进行分配,从而触发一次 Full GC,造成停顿。

使用场景

  • 追求停顿时间短的 B/S 系统

7. Garbage First(G1)收集器

G1 收集器可面向堆内存任何部分组成回收集进行回收,不再局限于新生代还是老年代。是垃圾收集器技术史上里程碑式的成果,JDK 7 Update 4 中正式商用。

G1 收集器基于 Region 堆内存布局,仍然是遵循分代收集理论设计,但 G1 不再以固定大小及数量划分分代区域,而是分成多个独立区域(Region)。每个 Region 根据需要扮演新生代的 Eden 空间、Survivor 空间或老年代空间,且 不需要连续,G1 收集器会对不同的角色进行不同策略的处理。

Region 中有一类特殊的 Humongous 区域,专门用来存储大对象。G1 收集器对大对象的定义为:超过一个 Region 容量一半的对象。当对象超过整个 Region 容量的超级大对象,将会被存放在多个连续的 Humongous 区域中,Humongous 区域被 G1 收集器当作老年代看待。

Region 是最小回收单元,每次回收的内存空间都是 Region 大小的整数倍。因此,G1 能建立可预测的停顿时间模型,有计划避免全区域垃圾收集(相当于 Full GC)。G1 收集器会根据回收所获得空间大小及所需时间的经验值,后台维护一个优先级列表,根据用户设定允许的收集停顿时间优先处理收益最大的 Region,故 G1 不追求一次性清理完整个堆,只需要保证收集速度跟得上对象分配速度。

设计思路

  • 局部收集
  • Region 内存布局

内存区域图

图7. Region 内存分区示意图

其中 E 为 Eden 区域,S 为 Survivor 区域,H 为 Humongous 区域。

工作图

图8. G1 收集器运行示意图.png

工作原理

1. 跨 Region 引用对象的解决

G1 收集器中,每个 Region 都会维护自己的记忆集,像其他收集器的记忆集一样,记忆集中记录下对其他 Region 的指针,还会记录别的 Region 对自己的指针,并标记这些指针在哪些卡页范围内,即为双向卡表。

G1 收集器的记忆集存储结构本质上是一种哈希表,Key 是别的 Region 的起始地址,Value 为一个集合,存储卡表的索引号。

因记忆集比其他传统收集器多很多,导致 G1 收集器有着更高的内存负担,根据经验,G1 至少需要 Java 堆容量 10% 到 20% 额外内存维持收集器工作。

2. 收集线程和用户线程的协调
  • 对象引用关系改变
    用户线程改变对象引用关系时,必须保证不能打破原本的对象图结构,导致标记错误,G1 采用 原始快照 算法实现。
  • 新对象创建
    G1 为每个 Region 设计了两个名为 TAMS 的指针,将 Region 中的一部分空间划分出来用于并发回收过程中新对象分配,并发回收时新分配对象地址都必须在这两个指针位置上。G1 默认在这个地址以上的对象是被隐式标记过,即默认存活,不纳入回收范围。
  • Full GC
    当内存回收速度赶不上内存分配速度时,G1 收集器和 CMS 收集器类似,会冻结用户线程执行,进行 Full GC 而产生停顿。
3. 建立可靠的停顿预测模型

用户通过 -XX:MaxGCPauseMillis 参数指定的停顿时间只意味着垃圾收集发生前的期望值。

G1 收集器停顿预测模型是以衰减均值为理论基础实现的,在垃圾收集过程中,G1 收集器会记录每个 Region 的回收耗时、每个 Region 记忆集中脏卡数量等各个可测量步骤花费的成本,并分析出平均值、标准偏差、置信度等统计信息。统计状态越新越能决定回收的价值,通过这些信息即可预测当前时间开始回收,通过哪些 Region 组成回收集可在不超过期望停顿时间的约束下获得最高收益。

工作流程

1. 初始标记

仅标记 GC Roots 能直接关联到的对象并修改 TAMS 指针的值,让下一个阶段用户线程并发运行时,能正确地在可用 Region 中分配新对象。速度较快,且是在 Minor GC 发生时同步完成,故实际上没有额外的停顿。

2. 并发标记

从 GC Roots 开始遍历整个对象图的过程,耗时较长但不影响用户线程运行。对象图扫描完后,还要重新处理 原始快照 记录下的在并发时有引用变动的对象。

3. 最终标记

对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量原始快照记录。

4. 筛选回收

负责更新 Region 的统计数据,对各个 Region 的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可自由选择任意多个 Region 构成回收集,然后把决定回收的那一部分 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程的,由多条收集器线程并行完成。

优点

  • 支持多线程并行收集
  • 停顿时间低,可动态设置预期值,且在延迟可控情况下,保证尽可能高的吞吐量
  • Region 内存布局使垃圾收集更加动态化,按回收收益动态回收
  • 不会产生内存空间碎片
    G1 整体看是基于标记 — 整理算法,但从 Region 局部看,是基于标记 — 复制算法,两种算法都无空间碎片。

缺点

  • 内存、CPU 负载都比 CMS 等其他收集器高

使用场景

  • 大内存应用
    根据经验,通常为堆容量 6GB 到 8GB 之间的应用,小内存应用中 CMS 收集器表现大概率好过 G1,但 G1 仍在不断更新优化中,未来可期。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容