JVM笔记——G1收集器概述

1. 简介

Garbage First(简称 G1)收集器是垃圾收集器技术发展史上里程碑式的成果:它开创了「面向局部收集」的设计思路和「基于 Region」的内存布局形式。

G1 收集器是一款主要面向服务端应用的垃圾收集器,其定位是「CMS 收集器的替代者和继承人」。它的发展简史如下:

2. 堆内存划分

虽然 G1 收集器也遵循分代收集理论,但其堆内存的布局与其他收集器有非常明显的差异:

G1 收集器的堆内存划分如图所示:

JVM笔记——G1收集器概述

3. 停顿时间模型

3.1 停顿时间模型

停顿时间模型(Pause Prediction Model):指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间大概率不超过 N 毫秒。

G1 收集器之所以能建立可预测的停顿时间模型,是因为它将 Region 作为单次回收的最小单元(每次收集到的内存空间都是 Region 大小的整数倍),这样可以有计划地避免整个 Java 堆进行全区域垃圾收集。

更具体的处理思路:让 G1 收集器去跟踪各个 Region 中的垃圾堆积的“价值”大小,然后在后台维护一个优先级列表,每次根据用户设定的收集停顿时间,优先处理回收价值收益最大的那些 Region(这就是“Garbage First”名字的由来)。

“价值”的衡量指标是:每次回收所获得的空间大小以及回收所需时间的经验值。

3.2 Mixed GC

G1 收集器之前的其他所有收集器(包括 CMS 收集器),垃圾收集的目标范围要么是整个新生代(Minor GC),或者整个老年代(Major GC),抑或整个 Java 堆(Full GC)。

而 G1 跳出了这个樊笼:它可以面向堆内存中任何部分来组成回收集(Collection Set,一般称 CSet)进行回收。衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多、回收收益最大。这就是 G1 收集器的 Mixed GC 模式。

4. 垃圾收集过程

G1 收集器的运行示意图如下:

JVM笔记——G1收集器概述

它的运作过程大致可分为以下四个步骤:

4.1 初始标记

TAMS:Top at Mark Start,Region 中的指针,用于并发标记时为对象分配内存空间。

4.2 并发标记

从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象。

此外,扫描完成后,还需要重新处理 STAB 记录下的在并发时有引用变动的对象。

STAB: Snapshot At The Begining,原始快照,参考前文「JVM笔记-HotSpot的算法细节实现」的 6.3 小节。

4.3 最终标记

4.4 筛选回收

5. 一些细节问题

5.1 跨 Region 引用对象

解决思路就是使用前文「JVM笔记-HotSpot的算法细节实现」第 4 小节的「记忆集」来避免全堆作为 GC Roots 扫描。

但是,G1 的记忆集更复杂,因为:

根据经验,G1 至少要耗费大约 Java 堆容量大小的 10%~20% 的额外内存空间来维持收集器工作。

5.2 并发标记

解决思路是前文第 6 小节分析的:CMS 收集器使用增量更新算法,而 G1 收集器则是通过原始快照(STAB)算法实现的。

此外,由于并发标记时用户线程仍在继续执行,肯定会持续创建新对象。

G1 为每个 Region 设计了两个名为 TAMS(Top at Mark Start)的指针,把 Region 中的一部分空间划分出来用于并发回收过程中的新对象分配(默认都是存活的,不纳入回收范围)。

需要注意的是:如果内存回收速度赶不上内存分配的速度,G1 收集器也要被迫冻结用户线程执行,导致 Full GC 而产生长时间“Stop The World”。

5.3 可靠的停顿模型

G1 收集器的停顿模型是以衰减均值(Decaying Average)为理论基础来实现的:垃圾收集过程中,G1 收集器会根据每个 Region 的回收耗时、记忆集中的脏卡数量等,分析得出平均值、标准偏差等。

“衰减平均值”比普通的平均值更能准确地代表“最近的”平均状态,通过这些信息预测现在开始回收的话,由哪些 Region 组成回收集才能在不超期望停顿时间的约束下获得最高收益。

6. G1 VS CMS

G1 收集器经常会被拿来与 CMS 收集器进行比较。

且不论 G1 的一些创新设计:可以指定最大停顿时间、分 Region 的内存布局、按收益动态确定回收集等,这里只对比一些其他较为通用的地方。

6.1 收集算法

G1 的这两种算法使其在运作期间不会产生内存空间碎片,垃圾收集完成后能提供规整的可用内存。而且这样有利于程序长时间运行(大对象分配内存时不容易因无法找到连续内存空间而提前触发下一次收集)。

6.2 内存占用

CMS 和 G1 都使用卡表来处理跨代指针,但 G1 的卡表实现更复杂,且 Region 较多(本文 5.1 小节)。

相比而言,CMS 的卡表相对简单,只有一份,只需处理老年代到新生代的引用。

与 CMS 相比,G1 的内存占用会更大。

6.3 额外执行负载

由于二者细节实现不同导致用户程序执行时负载会有不同。以写屏障为例:

G1 的写屏障比 CMS 要消耗更多的运算资源。因此,CMS 写屏障是同步操作,而 G1 则是采用类似消息队列的异步操作。

整体而言:

小内存应用上,CMS 大概率会优于 G1;

大内存应用上,G1 则很可能更胜一筹。

这个临界点大概是在 6~8G 之间(经验值)。

一些相关的虚拟机参数如下:

# 使用 G1 收集器
-XX:+UseG1GC


# 设置 Region 大小(范围 1~32M,且为 2 的 N 次幂)
-XX:G1HeapRegionSize


# 最大收集停顿时间(默认 200 毫秒)
-XX:MaxGCPauseMillis

本文主要内容小结如下:

JVM笔记——G1收集器概述

展开阅读全文

页面更新:2024-05-13

标签:前文   小节   屏障   线程   指针   停顿   算法   标记   模型   对象   大小   内存   垃圾   时间   笔记   用户   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top