我一直在我的一个项目中试验 G1GC 和 Java 8(Oracle JVM)。我的 GC 标志有效地:
-Xms64m
-Xmx1024m
-XX:+UseG1GC
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xloggc:/tmp/gc.log
-XX:+PrintAdaptiveSizePolicy
Run Code Online (Sandbox Code Playgroud)
我观察到堆的增长比我拥有的实时数据量大得多。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 %)]
Run Code Online (Sandbox Code Playgroud)
实际上,我的应用程序正在生成大量垃圾,因此花费在 GC 上的时间比例高于 10%,因此 G1 的人体工程学增加了堆大小。
使用 Parallel 收集器可以调整这个阈值-XX:GCTimeRatio(吞吐量目标),但从我在文档中看到的情况来看,G1 没有等效的标志。
对于并行收集器,Java SE 提供了两个基于实现应用程序指定行为的垃圾收集调优参数:最大暂停时间目标和应用程序吞吐量目标;请参阅并行收集器部分。(这两个选项在其他收集器中不可用。)请注意,这些行为无法始终满足。
我的问题是,除了降低最大堆大小之外,如何调整 G1GC 以减少内存占用?
在日志中,没有证据表明我超出了最大暂停时间目标,并且确实增加并不能解决问题。
这可能是对这个问题的欺骗:哪个 JVM 标志设置了 G1Ergonomics 日志中提到的 GC 开销阈值?,但看起来已经接受了错误的答案。(或者也许它只适用于旧版本的 JVM。)
概述:
在这篇 Oracle 文章(1) 中,您可以找到 G1 最重要的标志(包括-XX:MaxGCPauseMillis)。
该错误报告表明该GCTimeRatio标志也在 G1 中使用。
另请参阅此相关问题与解答(2)。
我认为您应该能够通过设置为更高的值来解决这个问题,或者如果您知道您的应用程序创建了很多(年轻的)垃圾,您可以使用有关年轻代-XX:MaxGCPauseMillis大小的设置。编辑:好的,对此要非常小心,(1)指出:*年轻代大小*:避免使用 -Xmn 选项或任何或其他相关选项(例如 -XX:NewRatio)显式设置年轻代大小。固定年轻代的大小会覆盖目标暂停时间目标。
(1)
重要的默认值:
G1 GC 是一种自适应垃圾收集器,其默认设置使其无需修改即可高效工作。以下是重要选项及其默认值的列表。此列表适用于最新的 Java HotSpot VM,版本 24。您可以通过在 JVM 命令行上输入以下带有更改设置的选项来调整和调整 G1 GC,以满足您的应用程序性能需求。
设置 G1 区域的大小。该值是 2 的幂,范围从 1MB 到 32MB。目标是根据最小 Java 堆大小拥有大约 2048 个区域。
设置所需最大暂停时间的目标值。默认值为 200 毫秒。指定的值不适合您的堆大小。
设置用作年轻代大小最小值的堆百分比。默认值为 Java 堆的 5%。这是一个实验性的标志。有关示例,请参阅“如何解锁实验性 VM 标志”。此设置替换 -XX:DefaultMinNewGenPercent 设置。此设置在 Java HotSpot VM build 23 中不可用。
设置用作年轻代大小最大值的堆大小百分比。默认值为 Java 堆的 60%。这是一个实验性的标志。有关示例,请参阅“如何解锁实验性 VM 标志”。此设置替换 -XX:DefaultMaxNewGenPercent 设置。此设置在 Java HotSpot VM build 23 中不可用。
设置 STW 工作线程的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,最多为 8。
如果逻辑处理器数量超过 8 个,则将 n 的值设置为逻辑处理器的大约 5/8。这在大多数情况下都适用,但较大的 SPARC 系统除外,其中 n 的值约为逻辑处理器的 5/16。
设置并行标记线程的数量。将 n 设置为并行垃圾收集线程 (ParallelGCThreads) 数量的大约 1/4。
设置触发标记周期的Java堆占用阈值。默认占用率为整个 Java 堆的 45%。
设置要包含在混合垃圾收集周期中的旧区域的占用阈值。默认占用率为 65%。这是一个实验性的标志。有关示例,请参阅“如何解锁实验性 VM 标志”。此设置替换 -XX:G1OldCSetRegionLiveThresholdPercent 设置。此设置在 Java HotSpot VM build 23 中不可用。
设置您愿意浪费的堆的百分比。当可回收百分比小于堆浪费百分比时,Java HotSpot VM 不会启动混合垃圾收集周期。默认值为 10%。此设置在 Java HotSpot VM build 23 中不可用。
设置标记周期后混合垃圾收集的目标数量,以收集最多具有 G1MixedGCLIveThresholdPercent 实时数据的旧区域。默认是 8 次混合垃圾回收。混合收集的目标是在这个目标数量之内。此设置在 Java HotSpot VM build 23 中不可用。
设置混合垃圾收集周期期间要收集的旧区域数量的上限。默认值为 Java 堆的 10%。此设置在 Java HotSpot VM build 23 中不可用。
设置保留空闲内存的百分比,以降低空间溢出的风险。默认值为 10%。当您增加或减少百分比时,请确保将 Java 堆总量调整相同的量。此设置在 Java HotSpot VM build 23 中不可用。
(2)
我的猜测是这recent GC overhead higher than threshold推动了 G1 的决定。您可以通过设置 来放松它-XX:GCTimeRatio=4,这将允许它占用相对于 GC 的应用程序时间的 20%,而不是 10%。
如果那太多了,你应该
允许它使用更多的 CPU 核心 - 这将更容易满足其暂停时间目标,这反过来又意味着它可以推迟收集更长时间,从而更容易满足吞吐量目标。是的,这确实意味着使用更多内核实际上可以使用更少的 CPU 周期。
放宽暂停时间目标,因此必须减少收集频率