文档章节

G1垃圾回收器介绍

robin-yao
 robin-yao
发布于 2017/06/19 22:44
字数 4129
阅读 1002
收藏 3

本文主要参照oracle官方文档,进行大概翻译的。不是一一详细对应的。

官方链接: http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html

G1(Garbage-First )收集器是一种server-style 回收器,主要面向多核,大内存的服务器。G1 在实现高吞吐的同时,也最大限度满足了GC 停顿时间可控的目标。在Oracle JDK7 update 4 及 以后的版本全面支持G1 回收器功能。G1收集器主要为有如下需求的程序设计:

  • 可以像CMS 收集器 能同时和应用线程 一起并发的执行;
  • 实现压缩空间时用更少的停顿时间;
  • 满足可预测的GC停顿时间需求;
  • 不要牺牲太多的吞吐性能;
  • 不需要占用更多的Java Heap;

未来 G1 计划要全面取代CMS的。G1相比CMS有更多的优势,G1是压缩型收集器,G1通过依赖regions分区,可以实现压缩更充分。这样消除大部分潜在的碎片问题。G1提供更精准的可预测的垃圾停顿时间,满足用户指定垃圾回收时间的需求。

G1 运行概况

老的垃圾回收器(serial, parallel, CMS) 都把heap分为三个固定内存大小的部分:young区,old区,permanent区。

输入图片说明

G1采用另外一种方式划分内存。heap被分为一系列相等大小的region,特定的region集合被指定为同一角色(eden,survivor,old),但数量不是固定的,提供一种更弹性的内存利用方式。

输入图片说明

G1以一种和CMS 相似的方式执行垃圾回收。G1执行通过在并发标记阶段决定heap内对象存活性。标记阶段后,G1 知道哪些regions 是更空的,然后它优先处理垃圾多的region。这也是为啥被叫做Garbage-First的原因。如名字一样,G1会集中在对那些可能全部被回收的堆空间。G1通过用停顿预测模型来满足用户自定义的停顿时间目标,它基于设定的停顿时间来选择要回收的regions数量。

G1清理那些被确认为成熟的可以清理的regions,通过从一个或多个region里,copy对象到另一个region里,中间伴随着压缩和清理内存的工作。清理过程在多核机器上都采用并行执行,来降低停顿时间,增加吞吐量。因此 G1在持续的运行中 能减少碎片,满足用户自定义停顿时间需求。这种能力是以往的回收器所不具备的。CMS回收器不能进行碎片压缩,ParallelOld 只能进行整堆的压缩,会导致较长的停顿时间。

值得注意的是:G1不是一个实时的收集器,它只是最大可能的来满足设定的停顿时间。G1会基于以往的收集数据,来评估用户指定的停顿时间可以回收多少regions。G1要通过模型评估出要收集的regions需要花费的时间,然后决定停顿时间内可以回收多少个regions。

G1 Footprint

如果你从ParallelOldGC 或 CMS 移到G1,你将会看到JVM将需要更大的内存。主要是因为是因为G1用到相关的统计数据结构如: Remembered Sets 和 Collection Sets。

Remembered Sets 主要是是对region内对象引用的跟踪。每个region都有一个RSet。Rset占用量小于总足迹大小的5%。

Collection Sets 是指代 将要被收集region的集合。GC过程中,CSet所有存活的数据将被整理(copied/moved)。这些region的集合可能是Eden区 surivor 或者 old区。CSet大小大概是JVM大小的1%。

G1适合的场景

G1主要面向的场景就是大内存,同时要求低的GC延迟场景。一般6GB大小的内存,稳定的预测停顿时间一般小于0.5秒。

现在在CMS或者ParallelOldGC上运行的程序,如果具备以下特点,迁移到G1将会获得更大的好处。

  • Full GC 时间太长 或太频繁;
  • 对象分配速度或者晋升比例变化明显;
  • 不希望GC 或者压缩停顿时间太长;

如果你现在用CMS 或者 ParallelOldGC ,并且你的程序运行很好,没有经历长时间垃圾回收停顿,建议就不用迁移。

Reviewing GC with the CMS

CMS收集器收集老年代,它通过把大部分回收过程和应用程序并发工作最大限度缩小停顿时间。CMS 不对存活对象进行copy和压缩,不会移动存活的对象。因此会产生碎片问题。

CMS Collection Phases

Phase Description
(1) 初始标记 (会stop the world) 老年代的对象会被做可达性标记,包括那些被年轻代里的对象引用的。停顿时间相对来说比较短。
(2) 并发标记阶段 和程序并发执行,遍历全部的老年代对象,标识可达性。从根对象开始标记对象可达性。 修改器会在阶段2,3,5中并发执行,在这个过程中,只要有对象被分配(包括晋升上来的对象)会被立刻标记活的对象。
(3) 重新标记(Stop the World Event) 由于Java应用 并发标记阶段 后 对 对象进行更新,导致一些对象没有错过,所以进行重新标记
(4) 并发清除阶段 清除标记阶段被标记为不可达的对象。
(5) 重置 为下次并发收集做清理准备。

The G1 Garbage Collector Step by Step

G1收集器采用一种不同的方式来进行分配heap。

1. G1 Heap Structure

堆被分为许多固定大小的内存区间。Region的大小是在JVM启动时指定的。一般JVM设定2000左右个region,大小可以从1Mb 到32Mb之间进行设定。

输入图片说明

2. G1 Heap Allocation

这些regions 会被逻辑映射到Eden, Survivor, and old generation 区。

如同所示,regions可以被分到Eden, survivor, and old generation regions里。除此之外,还有第四种类型的对象 叫做 Humongous regions.。它们是用来放置大对象的,一般这些对象超过 region大小的50%。这些对象一般是存储在连续的regions中。最后一种类型就是heap没有用到的内存。目前收集大对象还没有被优化,因此要避免创建大对象。

3. Young Generation in G1

堆被分为2000左右个region。size最小值是1Mb,最大值是32Mb。蓝色的代表old区,绿色代表年轻代。这些region不要求连续。
输入图片说明

4. A Young GC in G1

存活的对象被copy或移动到survior区。如果对象晋升年龄达到要求,会被直接晋升到old区。这个过程会发生stop the world。Eden区和Survivor的大小是被计算的为了下次young GC。统计信息会被保存,帮助计算回收区间大小的, 主要有像停顿时间之类的信息。这种方式使调整region大小很方便。

输入图片说明

5. End of a Young GC with G1

存活的对象已经被放在survivor区或者old区。

输入图片说明

总结,G1 young GC :

  • heap被分为一个个region;
  • Young generation由一系列非连续的region组成,使之很容易的按需调整区的大小;
  • Young GCs, 会发生 stop the world 事件. 所有的应用线程会被停掉;
  • Young GC是并发多线程执行的;
  • 存活的对象被copy到survivor区或着old区;

Old Generation Collection with G1

主要涉及以下几个阶段,下边的过程也是young GC 的一部分。

Phase Description
(1) Initial Mark(Stop the World Event) 该阶段会发生stop-the-world事件. 标记那些对old区可能有引用的survivor region。
(2) Root Region Scanning 扫描那些对old区有引用的survivor regions . 该阶段发生时,程序仍然继续执行。在young GC发生前,该阶段必须完成。
(3) Concurrent Marking 该阶段发现整个heap存活的对象。 该阶段和程序并行执行。该阶段可能会被young gc 打断。
(4) Remark(Stop the World Event) 完全完成对heap存活对象的标记。采用snapshot-at-the-beginning (SATB) 算法完成,比CMS用的算法更快。
(5) Cleanup (Stop the World Event and Concurrent) 执行对存活对象的统计,同时进行清除释放region,会发生stop-the-world。擦除 Remembered Sets也会发生stop-the-world。重置空的regions,并返回给空闲列表,这个是并发执行的。
(*) Copying(Stop the World Event) copy存活对象到空的region,也会发生stop-the-world。 如果只是young region copy ,GC 的log标记是 [GC pause (young)].如果old区和young区都发生copy,则log标记是 [GC Pause (mixed)].

G1 Old Generation Collection Step by Step

看完上面的定义的阶段,我们下面详细看G1收集器里 它们是如何与老年区交互的。

6. Initial Marking Phase

对象的初始标记是被 young gc捎带的。在gc log中是表识为GC pause (young)(inital-mark).
输入图片说明

7.Concurrent Marking Phase

如果空的regions是被发现,则被表识为“X”,在Remark阶段,它们将会被立刻清除。 同时 统计对象可达性的信息。
输入图片说明

8.Remark Phase

空的region会被移除并被回收。同时计算所有region的活性。

输入图片说明

9.Copying/Cleanup Phase

G1选择活性最低的regions,这些region可以被收集的更快。这个过程和young gc 的收集过程一样。只是GC log会标识不同,如果只是young region copy ,GC 的log标记是 [GC pause (young)].如果old区和young区都发生copy,则log标记是 [GC Pause (mixed)]

输入图片说明

10. After Copying/Cleanup Phase

选择中的regions已被收集并压缩到深蓝色region和深绿色region中,如图所示。

输入图片说明

Command Line Options and Best Practices

该阶段主要是看一下各种G1的产数选项。

Basic Command Line

开启G1回收器: -XX:+UseG1GC 下面是一个完整的启动java程序,并开启G1的demo命令:

java -Xmx50m -Xms50m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar

Key Command Line Switches

-XX:+UseG1GC - 告诉JVM开启G1回收器

-XX:MaxGCPauseMillis=200 - 设定最大的GC停顿时间。 这是一个软目标,JVM将近最大限度去实现。因此 这个停顿时间可能不会被满足。 默认值是 200 milliseconds.

-XX:InitiatingHeapOccupancyPercent=45 - 开始并发GC的百分比。G1 通过该比例来触发GC,这个比例是基于全部的heap,而不是单单某个分区。 默认值是45%。

最佳实践

当你使用G1时,你应该遵循的一些最佳实践。

  • 不用设置young区大小。通过 -Xmn设置young区的大小会敢于G1回收的默认行为。设定young大小会导致G1设定的停顿时间目标失效。G1将不能按需去扩缩young区。

  • 响应时间指标。不要用平均的响应时间来设置 XX:MaxGCPauseMillis=<N>, 设置一个满足的90%目标的时间。这意味着90%的用户请求将不经历过长的响应时间。 记住预设停顿时间只是个目标,不是都能被满足的。

  • Evacuation Failure: 当没有足够的空间供存活对象或者晋升对象用的时候,会发生晋升失败。这时如果用-XX:+PrintGCDetails 开启GC log ,会打印出 to-space overflow 。这时GC 仍然继续,以求释放空间,没有被copy成功的对象 ,将会直接放在老年区,在要收集区里的 所有对RSets的更新都要被重新计算。这些步骤花销都比价大。

  • 如何避免evacuation failure: 加大堆的大小;加大-XX:G1ReservePercent=n,来为 'to-space'预留更多空间;增加marking线程数,通过 -XX:ConcGCThreads=n 选项;

Complete List of G1 GC Switches

G1回收器的一些关键的开关参数。

Option and Default Value Description
-XX:+UseG1GC 开启G1回收器
-XX:MaxGCPauseMillis=n 设定最大的GC停顿时间。 这是一个软目标,JVM将近最大限度去实现。因此 这个停顿时间可能不会被满足。
-XX:InitiatingHeapOccupancyPercent=n 开始并发GC的百分比。G1 通过该比例来触发GC,这个比例是基于全部的heap,而不是单单某个分区。 默认值是45%。
-XX:NewRatio=n Ratio of new/old generation sizes. The default value is 2.
-XX:SurvivorRatio=n Ratio of eden/survivor space size. The default value is 8.
-XX:MaxTenuringThreshold=n Maximum value for tenuring threshold. The default value is 15.
-XX:ParallelGCThreads=n 设置并行回收阶段,GC所要用到的线程数。默认值随着JVM所运行的平台不同而变化。
-XX:ConcGCThreads=n 设置GC并发的线程数。默认值随着JVM所运行的平台不同而变化。
-XX:G1ReservePercent=n 加大-XX:G1ReservePercent=n,来为 'to-space'预留更多空间,防止晋升失败。默认值是10。
-XX:G1HeapRegionSize=n 指定region的大小。默认值是根据所开堆空间大小来计算的。region最大值是32Mb,最小值是1Mb。

Logging GC with G1

下面我们会介绍一些G1收集器的log信息。这个章节将会大概浏览一些日志信息相关的内容。

Setting the Log Detail

设定日志详情,有三种不同方式; (1) -verbosegc (等价与 -XX:+PrintGC) :输出的日志格式信息如下:

[GC pause (G1 Humongous Allocation) (young) (initial-mark) 24M- >21M(64M), 0.2349730 secs]
[GC pause (G1 Evacuation Pause) (mixed) 66M->21M(236M), 0.1625268 secs] 
 

(2) -XX:+PrintGCDetails : 更加详细的输出,包括每阶段的平均值,最大,最小时间 ;根扫描, RSet更新, RSet 扫描, 对象Copy,也会显示Eden, Survivors 及 整个 Heap 的占用比. 如下:

[Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7]
[Eden: 818M(818M)->0B(714M) Survivors: 0B->104M Heap: 836M(4096M)->409M(4096M)]

(3) -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest 设置最详细的输出级别,如下

[Ext Root Scanning (ms): 2.1 2.4 2.0 0.0 Avg: 1.6 Min: 0.0 Max: 2.4 Diff: 2.3]
[Update RS (ms):  0.4  0.2  0.4  0.0   Avg: 0.2 Min: 0.0 Max: 0.4 Diff: 0.4]
[Processed Buffers : 5 1 10 0  Sum: 16, Avg: 4, Min: 0, Max: 10, Diff: 10]

时间显示 一系列的时间控制如下:

(1) -XX:+PrintGCTimeStamps - 显示自从JVM开始的时间.

Sample Output

  1.729: [GC pause (young) 46M->35M(1332M), 0.0310029 secs]

(2) -XX:+PrintGCDateStamps - 加上日期前缀.

2012-05-02T11:16:32.057+0200: [GC pause (young) 46M->35M(1332M), 0.0317225 secs]

G1 Logging 术语

Parallel Time

414.557: [GC pause (young), 0.03039600 secs] [Parallel Time: 22.9 ms]
[GC Worker Start (ms): 7096.0 7096.0 7096.1 7096.1 706.1 7096.1 7096.1 7096.1 7096.2 7096.2 7096.2 7096.2 Avg: 7096.1, Min: 7096.0, Max: 7096.2, Diff: 0.2]

Parallel Time – 停顿回收时并行花费的时间; Worker Start – Worker开始工作的时间戳;

External Root Scanning

[Ext Root Scanning (ms): 3.1 3.4 3.4 3.0 4.2 2.0 3.6 3.2 3.4 7.7 3.7 4.4
     Avg: 3.8, Min: 2.0, Max: 7.7, Diff: 5.7]

External root scanning - 扫描外部根花费的时间(如)system dictionary ;

Update Remembered Set

[Update RS (ms): 0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 Avg: 0.0, Min: 0.0, Max: 0.1, Diff: 0.1]
   [Processed Buffers : 26 0 0 0 0 0 0 0 0 0 0 0
    Sum: 26, Avg: 2, Min: 0, Max: 26, Diff: 26]

Update Remembered Set - 更新时间依赖于region里cards的秘密,cards越多,花费时间越长;

Scanning Remembered Sets

[Scan RS (ms): 0.4 0.2 0.1 0.3 0.0 0.0 0.1 0.2 0.0 0.1 0.0 0.0 Avg: 0.1, Min: 0.0, Max: 0.4, Diff: 0.3]

Scanning Remembered Sets - 扫描RSets集合;

Object Copy


[Object Copy (ms): 16.7 16.7 16.7 16.9 16.0 18.1 16.5 16.8 16.7 12.3 16.4 15.7 Avg: 16.3, Min: 12.3, Max:  18.1, Diff: 5.8]

Object copy – 对象copy 迁移花费的时间;

Termination Time

[Termination (ms): 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0] [Termination Attempts : 1 1 1 1 1 1 1 1 1 1 1 1 Sum: 12, Avg: 1, Min: 1, Max: 1, Diff: 0]

Termination time - 当一个线程完成它的对象copy和scan任务后,它进入 termination protocol。它寻找任务去窃取,一旦完成窃取到的任务,它又进入 termination protocol 状态。 Termination attempt记录统计尝试的次数。

GC Worker End

[GC Worker End (ms): 7116.4 7116.3 7116.4 7116.3 7116.4 7116.3 7116.4 7116.4 7116.4 7116.4 7116.3 7116.3
    Avg: 7116.4, Min: 7116.3, Max: 7116.4, Diff:   0.1]
[GC Worker (ms): 20.4 20.3 20.3 20.2 20.3 20.2 20.2 20.2 20.3 20.2 20.1 20.1
     Avg: 20.2, Min: 20.1, Max: 20.4, Diff: 0.3]

GC worker end time – GC worker停止的时间戳;

GC worker time – GC worker线程工作的时间;

Clear CT

[Clear CT: 0.6 ms]

清理 card table of RSet scanning meta-data 花费时间

Other

[Other: 6.8 ms]

GC暂停的其他顺序阶段所需的时间。

CSet


[Choose CSet: 0.1 ms]

分析哪些regions需要回收花费的时间,也就是计算CSets;

Ref Proc

[Ref Proc: 4.4 ms]

处理 soft, weak, etc. references 花费的时间;

Ref Enq

[Ref Enq: 0.1 ms]

Time spent placing soft, weak, etc. references on to the pending list.

Free CSet

[Free CSet: 2.0 ms]

清理CSet的花费的时间,及包括清理它们里面的RSet。

G1大概基本先介绍到这里。

本文链接https://my.oschina.net/robinyao/blog/983822

© 著作权归作者所有

上一篇: JDK BitSet实现原理
下一篇: Java Reference详解
robin-yao
粉丝 167
博文 54
码字总数 61436
作品 0
杭州
私信 提问
五分钟了解Java10针对垃圾收集的改进

Java10 已经发布了大概有一个多月了。我们在之前的文中介绍过10为我们带来的一些新特性:JDK10要来了:下一代 Java 有哪些新特性?。其中就提到了10 关于G1垃圾收集器的一些改进。G1在Java ...

ImportSource
2018/04/15
0
0
JVM(五)垃圾回收器的前世今生

全文共 2195 个字,读完大约需要 8 分钟。 如果垃圾回收的算法属于内存回收的方法论的话,那本文讨论的垃圾回收器就属于内存回收的具体实现。 因为不同的厂商(IBM、Oracle),实现的垃圾回收...

王磊的博客
02/13
23
0
JVM系列第9讲:JVM垃圾回收器

前面文章中,我们介绍了 Java 虚拟机的内存结构,Java 虚拟机的垃圾回收机制,那么这篇文章我们说说具体执行垃圾回收的垃圾回收器。 总的来说,Java 虚拟机的垃圾回收器可以分为四大类别:串...

陈树义
2018/11/22
0
0
JVM系列篇:深入剖析G1收集器+回收流程+推荐用例

本系列会持续更新。 金三已经过去一半了,即将进入面试的高峰期。在BAT面试中,JVM基本都是必考的系列。你至少需要掌握:JVM内存模型与JVM参数详细配置、JVM的4种垃圾回收算法、垃圾回收机制...

mikechen优知
03/26
200
0
Java垃圾回收机制-垃圾收集器(二)

上篇总结了常见的垃圾收集算法,这里回顾下常见的垃圾收集器。 上图展示了7种不同分代的垃圾收集器,如果两个收集器之间存在连线,说明他们之间可以搭配使用。虚拟机所处的区域,代表它是新生...

Real_man
2018/06/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

什么是线程死锁,如何解决

产生死锁的条件有四个: 互斥条件:所谓互斥就是进程在某一时间内独占资源。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得资源,在末使用完...

苏坡吴
34分钟前
4
0
CSS

一、CSS概述 1、什么是CSS Cascading style sheets,层叠样式表、级联样式表,简称样式表 2、css的作用 设置HTML网页中元素的样式 3、HTML与CSS的关系 HTML:负责网页的搭建,内容展示--网页...

wytao1995
45分钟前
4
0
二叉查找树的第 K 个结点

private TreeNode ret;private int cnt = 0;public TreeNode KthNode(TreeNode pRoot, int k) { inOrder(pRoot, k); return ret;}private void inOrder(TreeNode root......

Garphy
今天
4
0
windo8 weblogic

需要的软件包 现在安装jdk 则先进入你电脑自带jdk \bin目录下 然后java -jar 执行你的jar包就可以了 欢迎界面直接点击下一步,跳到更新界面,直接选择跳过 然后选择安装目录(注意:目录不要有...

恩多
今天
8
0
Activiti 批注

Activiti添加批注(comment)信息 在每次提交任务的时候需要描述一些批注信息,例如:请假流程提交的时候要描述信息为什么请假,如果领导驳回可以批注驳回原因等  1、添加批注 // 由于流程...

奔跑的android
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部