如何调整G1GC以减小内存占用?

7

我一直在我的项目中尝试使用Java 8(Oracle JVM)的G1GC。我的GC标志如下:

-Xms64m
-Xmx1024m
-XX:+UseG1GC
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xloggc:/tmp/gc.log
-XX:+PrintAdaptiveSizePolicy

我注意到堆比我拥有的活动数据要大得多。 GC日志显示了我认为是根本原因:

[G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 10.17 %, threshold: 10.00 %, uncommitted: 811597824 bytes, calculated expansion amount: 162319564 bytes (20.00 %)]

实际上,我的应用程序产生了大量垃圾,因此花费在GC上的时间比例高于10%,因此G1的人体工学增加了堆大小。
对于并行收集器,可以使用 -XX:GCTimeRatio (吞吐量目标)调整此阈值,但从我在文档中看到的情况来看,G1没有等效的标志。

对于并行收集器,Java SE提供了两个基于实现指定应用程序行为的垃圾收集调整参数:最大暂停时间目标和应用程序吞吐量目标。请参见“ 并行收集器”部分。(这两个选项在其他收集器中不可用。)请注意,这些行为并非总是可以实现。

我的问题是,除了降低最大堆大小外,如何为较小的内存占用调整G1GC?
在日志中没有证据表明我失误地达到了最大暂停时间目标,而且增加它也不能解决问题。
这可能是这个问题的副本:哪个JVM标志设置了G1Ergonomics日志中提到的GC开销阈值?,但在那里,似乎接受了一个错误的答案。(或者它仅对旧版JVM正确。)

1
你是否真的看到堆大小在增加(进程在你的计算机上的RAM使用情况,或列出堆大小的JVM mbean指标)?它仍然遵守最大堆大小吗? - rogerdpack
1
花费数小时来节省几美元的硬件可能不值得。节省1 GB内存有多重要? - Peter Lawrey
3
@Peter 当然这并不划算;但如果我们对此感兴趣,那就不应该阻止我们尝试学习技术的工作原理。 - pauldoo
2个回答

5

概述:

  • 这篇Oracle文章(1)中,可以找到G1的最重要标志(包括-XX:MaxGCPauseMillis)。

  • 这篇错误报告表明GCTimeRatio标志也在G1中使用。

  • 请看这个相关问题和回答(2)。

  • 我认为您应该通过将-XX:MaxGCPauseMillis设置为较高值来解决此问题,或者如果您知道您的应用程序会创建大量(年轻)垃圾,则可以尝试调整年轻代大小的设置。 编辑:好的,请非常小心,(1)指出:*年轻代大小*:避免使用 -Xmn 选项或任何其他相关选项(例如-XX:NewRatio)显式设置年轻代大小。固定年轻代大小会覆盖目标暂停时间目标。


(1)

重要的默认值:

G1 GC是一种自适应垃圾收集器,具有默认值,可在不进行修改的情况下有效地工作。以下是重要选项及其默认值列表。此列表适用于最新版本的Java HotSpot VM,版本号为24。您可以通过在JVM命令行上输入以下选项并更改设置来适应和调整G1 GC至您的应用程序性能需求。

  • -XX:G1HeapRegionSize=n

设置G1区域的大小。该值将为2的幂,并且范围从1MB到32MB。目标是基于最小Java堆大小拥有大约2048个区域。

  • -XX:MaxGCPauseMillis=200

设置所需最大暂停时间的目标值。默认值为200毫秒。指定的值不会根据您的堆大小进行调整。

  • -XX:G1NewSizePercent=5

设置堆使用百分比作为年轻代大小的最小值。默认值为Java堆的5%。这是一个实验性的标志。请参见“如何解锁实验性VM标志”以获取示例。此设置替换了-XX:DefaultMinNewGenPercent设置。此设置在Java HotSpot VM,版本23中不可用。

  • -XX:G1MaxNewSizePercent=60
设置年轻代最大堆大小占堆大小的百分比。默认值为Java堆的60%。这是一个实验标志。请参阅“如何解锁实验VM标志”作为示例。此设置替换了-XX:DefaultMaxNewGenPercent设置。此设置在Java HotSpot VM,build 23中不可用。
-XX:ParallelGCThreads = n
设置STW工作线程的值。将n的值设置为逻辑处理器的数量。对于小于等于8个逻辑处理器的情况,n的值与逻辑处理器的数量相同。
如果有超过八个逻辑处理器,则将n的值设置为逻辑处理器的约5/8。这在大多数情况下都有效,但对于更大的SPARC系统,n的值可以约为逻辑处理器的5/16。
-XX:ConcGCThreads = n
设置并行标记线程的数量。将n设置为并行垃圾收集线程(ParallelGCThreads)数量的约1/4。
-XX:InitiatingHeapOccupancyPercent = 45
设置触发标记周期的Java堆占用率阈值。默认占用率为整个Java堆的45%。
-XX:G1MixedGCLiveThresholdPercent = 65
设置要包含在混合垃圾回收周期中的旧区域的占用率阈值。默认占用率为65%。这是一个实验标志。请参阅“如何解锁实验VM标志”作为示例。此设置替换了-XX:G1OldCSetRegionLiveThresholdPercent设置。此设置在Java HotSpot VM,build 23中不可用。
-XX:G1HeapWastePercent = 10
设置您愿意浪费的堆百分比。当可回收百分比小于堆浪费百分比时,Java HotSpot VM不会启动混合垃圾回收周期。默认值为10%。此设置在Java HotSpot VM,build 23中不可用。
-XX:G1MixedGCCountTarget = 8
设置标记周期后要收集具有最多G1MixedGCLIveThresholdPercent实时数据的旧区域的混合垃圾收集次数的目标数。默认值为8个混合垃圾收集。混合收集的目标是在此目标数内。此设置在Java HotSpot VM,build 23中不可用。
-XX:G1OldCSetRegionThresholdPercent = 10
设置在混合垃圾回收周期期间要收集的旧区域数量的上限。默认值为Java堆的10%。此设置在Java HotSpot VM,build 23中不可用。
-XX:G1ReservePercent = 10
设置您愿意浪费的堆的百分比。当可回收百分比小于堆浪费百分比时,Java HotSpot VM不会启动混合垃圾回收周期。默认值为10%。此设置在Java HotSpot VM,build 23中不可用。
设置保留内存百分比以减少 to-space 溢出的风险。默认值为 10%。在增加或减少百分比时,请确保按相同数量调整总 Java 堆大小。此设置在 Java HotSpot VM,build 23 中不可用。
(2)
我猜测 "recent GC overhead higher than threshold" 驱动了 G1 的决策。您可以通过设置 "-XX:GCTimeRatio=4" 来放宽它,这将允许其占用相对于应用程序时间的 20% 的 CPU 循环进行垃圾回收,而不是 10%。
如果这太多了,您应该:
  • 允许其使用更多的 CPU 核心——这将使它更容易实现暂停时间目标,从而可以推迟更长时间的收集,使其更容易实现吞吐量目标。 是的,这确实意味着使用更多核心实际上可以减少总 CPU 循环。
  • 放松暂停时间目标,使其需要更少的收集

1

经过进一步调查,发现G1的人机工程学确实尊重-XX:GCTimeRatio,尽管文档上说的不是这样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接