Java G1内存回收系列第二章

在上一篇《Java G1垃圾收集器工作原理和特性》的文章中,讨论了G1垃圾回收体系结构和工作原理,接着上一篇文章继续G1的话题。

Summary of Old Generation GC

上一篇文章中对G1作了全面的介绍,因此,可以对G1上的老年代垃圾收集器作出几点总结。

Concurrent Marking Phase

在应用程序运行时,将同时计算活跃度信息。该活跃度信息用于确认,在一个疏散(移动对象)暂停阶段最佳的回收区域。没有类似于CMS的sweeping阶段。重新标记阶段,使用Snapshot-at-the-Beginning(SATB)算法,该算法比CMS用的算法快得多,完全空的区域会被回收。

Copying/Cleanup Phase

年轻代和老代会同时被收回,年老代区域,是根据他们之上的对象的活跃度划分的。

Command Line Options and Best Practices(命令行选项和最佳实践)

在本节中,来看看G1的各种命令行选项。

基本的命令行:

To enable the G1 Collector use: -XX:+UseG1GC

打开G1收集器,示例:

Here is a sample command line for starting the Java2Demo included in the JDK demos and samples download:

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

重点命令行

-XX:+UseG1GC - Tells the JVM to use the G1 Garbage collector.

告诉JVM使用G1收集器

-XX:MaxGCPauseMillis=200

- Sets a target for the maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it. Therefore, the pause time goal will sometimes not be met. The default value is 200 milliseconds.

设置最大的暂停时间,这不是一个硬性要求,所以,JVM只是尽最大限定的去做完成,因此,有时并不能保证暂停时间百分百准确,默认值为200毫秒。

-XX:InitiatingHeapOccupancyPercent=45

- Percentage of the (entire) heap occupancy to start a concurrent GC cycle. 
It is used by G1 to trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations. A value of 0 denotes 'do constant GC cycles'. The default value is 45 (i.e., 45% full or occupied).

启动周期性并发GC时,堆空间的占用比,G1中整个堆空间的使用比,会触发周期性并发GC,值为0表示“GC执行周期恒定”。默认值为45(即,使用占比达45%)。

Best Practices(最佳实践)

使用G1时,可以遵循一些最佳实践。

Do not Set Young Generation Size    

通过-Xmn参数明确指定G1年轻代的大小,以此来干预G1收集器的默认行为。G1将不再遵守约定的暂定时间,实际上,设置年轻代大小会禁用约定暂停时间。

Response Time Metrics

不使用平均响应时间(ART),作为权衡来设置XX:MaxGCPauseMillis=<N>,而是考虑此值的设置在满足时间要求方面达到90%以上。这里90%是指用户发起一个请求,不会感觉到响应时间过高或超出预期目标。 记住,暂停时间是一个预期目标,并不能保证始终满足。

What is an Evacuation Failure(什么是垃圾回收失败)

一个所谓的晋升失败发生在GC期间,新的交换区和晋升对象耗尽堆中的区域块,而堆空间因达到最大而无法扩展。这一点可以通过使用-XX:+PrintGCDetails by to-space overflow在GC日志中看到,这是昂贵的操作!GC 需要继续工作, 所以空间必须被释放。未成功复制的对象会就地变成终身的。对CSet中区域的rset的任何更新都必须重新生成(Any updates to RSets of regions in the CSet have to be regenerated.)。所有这些步骤的代价都很高。

How to avoid Evacuation Failure(如何避免垃圾回收)

为避免垃圾回收失败, 请考虑以下选项。

1、加大堆空间。

2、增加的方法-XX:G1ReservePercent=n,默认值为10。

3、G1创建了一个假的的上限,试图让备用空间保持空闲,以备真正需要。

4、提前开始标记工作。

5、增加标记线程的参数项为:-XX:ConcGCThreads=n。

Complete List of G1 GC Switches(G1 GC开关的完整列表)

这是G1 GC开关的完整列表。 请记住使用上面列出的最佳实践。

Option and Default Value    Description

-XX:+UseG1GC

Use the Garbage First (G1) Collector

-XX:MaxGCPauseMillis=n

设置一个GC暂停时间的上限,这是一个参考值,JVM将尽可能的去满足这个目标。

-XX:InitiatingHeapOccupancyPercent=n

启动周期性并发GC时,整个堆的占用比,周期性并发GC会基于堆的使用比例而被触发,为0表示 "恒定的GC周期",默认值为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

设置在垃圾回收器的并行阶段使用的线程数。默认值随 JVM 运行的平台而异。

-XX:ConcGCThreads=n

并发垃圾回收器将使用的线程数。默认值随 JVM 运行的平台而异。

-XX:G1ReservePercent=n

设置堆中保留空间的数量, 以减少对象晋升失败的可能性。默认值为10。

-XX:G1HeapRegionSize=n

G1中Java 堆被细分为均匀大小的区域。此参数用于设置各个区域的大小。此参数的默认值,是根据堆大小视情况而定。最小值为 1Mb, 最大值为32Mb。

Logging GC with G1

最后一个主题是,使用日志信息来分析G1收集器的性能。本节简要概述了可用于收集数据的开关,以及日志中打印的信息。

Setting the Log Detail

可以将详细信息设置为三个不同级别的明细数据。

(1) -verbosegc(相当于-XX:+PrintGC)将细节日志设置为可用。

Sample Output(样例输出)

[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

将详情级别设置为更多,这些选项显示了以下信息:

1、每个阶段的平均、最小和最大时间。

2、根扫描, RSet 更新 (处理缓冲区信息), RSet 扫描, 对象复制, 终止 (尝试次数)。

3、还显示 "其他" 时间, 如选择 CSet、引用处理、引用进行排队和释放 CSet 的时间。

4、显示新生代、交换区和堆的总占用。

Sample Output(样例输出)

[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

将细节级别设置为最佳,类似于fine级别,但还包含单独的worker线程信息。

[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]

Determining Time

两个开关决定GC日志中时间显示的方式

(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]

Understanding G1 Log

为了理解日志,这里定义了一些术语用于真实的GC日志输入,以下这些示例显示了日志用到的术语并从中找到想要的。

To understand the log, this section defines a number of terms using actual GC log output. The following examples show output from the log with explanations of the terms and values you will find in it.

Note: For more information check out Poonam Bajaj's Blog post on G1 GC logs.

G1 Logging Terms Index

Clear CT

CSet

External Root Scanning

Free CSet

GC Worker End

GC Worker Other

Object Copy

Other

Parallel Time

Ref Eng

Ref Proc

Scanning Remembered Sets

Termination Time

Update Remembered Set

Worker Start

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 – 线程开始工作的时间戳。
Note: 每个条目上的线程ID都是经过排序的。

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 - 扫描外部根所需的时间(e.g., things like system dictionary that point into the heap.)

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 - 在暂停开始之前已完成,但尚未由细化线程并发处理的,缓冲区都必须更新。时间取决于卡片(记录跨代引用/跨多个区域引用)的密度。卡片越多, 花的时间就越长。

Any buffers that are completed but have not yet been processed by the concurrent refinement thread before the start of the pause have to be updated. Time depends on density of the cards. The more cards, the longer it will take.

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]F

Scanning Remembered Sets -查找指向用于回收的指针。Look for pointers that point into the Collection Set.

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 – 每个线程用于复制和处理对象的时间。The time that each individual thread spent copying and evacuating objects.

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 - 当每个线程完成其特定集合上,对象扫描和复制工作时, 它会反复进行寻找可以分担的任务,直到任务被完成。

When a worker thread is finished with its particular set of objects to copy and scan, it enters the termination protocol. It looks for work to steal and once it's done with that work it again enters the termination protocol. Termination attempt counts all the attempts to steal work.

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 工作停止时的时间戳。 Timestamp when the individual GC worker stops.

GC worker time

单个 GC 辅助线程所占用的时间。Time taken by individual GC worker thread.

GC Worker Other

[GC Worker Other (ms): 2.6 2.6 2.7 2.7 2.7 2.7 2.7 2.8 2.8 2.8 2.8 2.8
    Avg: 2.7, Min: 2.6, Max: 2.8, Diff: 0.2]

GC worker other

这个时间不属于前面所提到的,线程的工作时间(对每个GC线程而言),这个值应该很低,以往,看到了过高的值, 它们被归咎于 JVM 其他部分的瓶颈(e.g., increases in the Code Cache occupancy with Tiered).

The time (for each GC thread) that can't be attributed to the worker phases listed previously. Should be quite low. In the past, we have seen excessively high values and they have been attributed to bottlenecks in other parts of the JVM (e.g., increases in the Code Cache occupancy with Tiered).

Clear CT

[Clear CT: 0.6 ms]

清除 RSet 扫描元数据卡表所用的时间。

Time taken to clear the card table of RSet scanning meta-data.

Other

[Other: 6.8 ms]

为 GC 暂停的各种其他连续阶段所用的时间。

Time taken for various other sequential phases of the GC pause.

CSet

[Choose CSet: 0.1 ms]

完成要收集的区域集的时间,通常很小,年老代会稍微长一点。

Time taken finalizing the set of regions to collect. Usually very small; slightly longer when having to select old.

Ref Proc

[Ref Proc: 4.4 ms]

前一次GC处理软引用、弱引用等相关延迟引用所用的时间。

Time spent processing soft, weak, etc. references deferred from the prior phases of the GC.

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]

垃圾回收时用于释放内存空间所用的时间。

Time spent freeing the set of regions that have just been collected, including their remembered sets.

Summary

在本OBE中,已经了解了Java JVM中包含的G1垃圾收集器。 

首先,了解了堆和垃圾收集器是如何成为Java JVM的关键部分。 接着,使用CMS收集器和G1收集器查看垃圾收集的工作原理。接下来,了解了G1命令行开关以及使用它们的最佳实践。最后,了解了记录GC日志中包含的对象和数据。

总结

在本教程中了解到:

Java JVM的组件

G1收集器概述

CMS收集器的评论

回顾G1收藏家

命令行开关和最佳实践

使用G1进行记录

Resources

有关更多信息和相关信息,请参阅这些网站和链接。