[HotSpot 虚拟机垃圾收集调优指南-JavaSE11-笔记]-7-Garbage-First(G1)垃圾收集器如何调优?

7-Garbage-First(G1)垃圾收集器如何调优???

7.1 使用G1 的一般建议

一般的建议是使用 G1 的默认设置,最终给它一个不同的暂停时间目标,并 -Xmx根据需要设置最大 Java 堆大小。

G1 默认值的平衡方式与其他收集器不同。G1 在默认配置中的目标既不是最大吞吐量也不是最低延迟,而是在高吞吐量下提供相对较小、均匀的暂停。然而,G1 增量回收堆空间暂停时间控制的机制在应用程序线程和空间回收效率方面都会产生一些开销。

如果您更喜欢高吞吐量,请通过使用-XX:MaxGCPauseMillis 提供更大的堆放宽暂停时间目标

如果延迟是主要要求,则修改暂停时间目标避免通过使用诸如 和其他选项配置将年轻代大小限制为特定值-Xmn -XX:NewRatio因为年轻代大小是 G1 允许其满足暂停时间的主要手段。将年轻代大小设置为单个值会覆盖并实际上禁用暂停时间控制

7.2 从其他收集器迁移到 G1

通常,当从其他收集器(尤其是并发标记清除收集器)迁移到 G1 时首先删除所有影响垃圾收集的配置选项,并且仅通过使用-Xmx和可选设置暂停时间目标和总体堆大小-Xms

许多对其他收集器以某种特定方式响应有用的选项,要么根本没有效果,要么甚至降低吞吐量和满足暂停时间目标的可能性。一个例子可能是设置年轻代大小,完全阻止 G1 调整年轻代大小以满足暂停时间目标。

7.3 如何提高 G1 性能

G1 旨在提供良好的整体性能,而无需指定其他选项。但是,在某些情况下,它们的默认启发式或默认配置提供次优结果。本节提供了有关在这些情况下进行诊断和改进的一些指南。本指南仅描述 G1 在给定应用程序时提供的在选定指标中提高垃圾收集器性能的可能性。根据具体情况,应用程序级别的优化可能比尝试调整 VM 以更好地执行更有效,例如,通过完全减少寿命较短的对象来避免一些有问题的情况

出于诊断目的,G1 提供了全面的日志记录。一个好的开始是使用该-Xlog:gc*=debug选项,然后在必要时从中优化输出。该日志提供了有关垃圾收集活动的暂停期间和之外的详细概述。这包括收集的类型和在暂停的特定阶段花费的时间细分。

以下小节探讨了一些常见的性能问题。

7.3.1 观察完整的垃圾收集

全堆垃圾回收(Full GC)通常非常耗时。老年代堆占用过高导致的Full GC可以通过在日志中找到*Pause Full (Allocation Failure)字样来检测。*Full GC 通常在垃圾收集之前进行,这些垃圾收集遇到由to-space exhausted标记指示的疏散失败。

发生 Full GC 的原因应用程序分配了太多无法足够快地回收的对象。通常并发标记无法及时完成以启动空间回收阶段。遇到 Full GC 的可能性可能会因分配许多巨大的对象而更加复杂。由于这些对象在 G1 中的分配方式,它们可能会占用比预期更多的内存。

目标应该是确保并发标记按时完成。这可以通过降低老年代的分配率来实现,或者给并发标记更多的时间来完成。

G1 为您提供了几个选项来更好地处理这种情况

  • gc+heap=info您可以使用日志记录来确定 Java 堆上巨大对象占用的区域数 。Y在“ Humongous regions: X->Y”行中为您提供巨大对象占用的区域数量。如果此数字与旧区域的数量相比较高,则最佳选择是尝试减少此对象数量。您可以通过增加区域来实现size 使用 -XX:G1HeapRegionSize选项。当前选择的堆区域大小打印在日志的开头。
  • 增加 Java 堆的大小。这通常会增加标记必须完成的时间量。
  • -XX:ConcGCThreads通过显式 设置来增加并发标记线程的数量。
  • 强制 G1 提前开始标记。G1 根据早期的应用程序行为自动确定启动堆占用百分比 (IHOP) 阈值。如果应用程序行为发生变化,这些预测可能是错误的。有两个选项: 通过修改 增加自适应 IHOP 计算中使用的缓冲区来降低何时开始空间回收的目标占用率-XX:G1ReservePercent-XX:-G1UseAdaptiveIHOP或者,通过使用和手动设置来禁用 IHOP 的自适应计算 -XX:InitiatingHeapOccupancyPercent

Full GC 的分配失败以外的其他原因通常表明应用程序或某些外部工具导致了完整的堆收集。如果原因是 System.gc(),并且无法修改应用程序源,则可以通过使用来减轻 Full GC 的影响, -XX:+ExplicitGCInvokesConcurrent或者通过设置让 VM 完全忽略它们-XX:+DisableExplicitGC。外部工具可能仍会强制进行 Full GC;只能通过不请求它们来删除它们。

7.3.2 大对象产生的碎片

由于需要为它们找到一组连续的区域,因此可能会在所有 Java 堆内存耗尽之前发生 Full GC。在这种情况下,潜在的选项是通过使用选项-XX:G1HeapRegionSize来减少巨大对象的数量或增加堆的大小来增加堆区域的大小。在极端情况下,可能没有足够的连续空间可供 G1 分配对象,即使可用内存另有说明也是如此。如果 Full GC 无法回收足够的连续空间,这将导致 VM 退出。因此,除了如前所述减少巨大对象分配的数量或增加堆外,没有其他选择。

7.3.3 调整延迟

本节讨论在常见延迟问题(即暂停时间太长)的情况下改进 G1 行为的提示。

7.3.3.1 异常系统或实时使用

对于每个垃圾收集暂停,gc+cpu=info日志输出都包含一行,其中包含来自操作系统的信息,其中包含有关暂停时间在何处花费的细分。这种输出的一个例子是User=0.19s Sys=0.00s Real=0.01s

用户时间是花在 VM 代码上的时间
system time在操作系统中花费的时间
并且real time暂停期间经过的绝对时间量

如果系统时间相对较高,那么大多数情况下是环境造成的

高系统时间的常见已知问题是:

  • VM 从操作系统内存分配或归还内存可能会导致不必要的延迟-Xms通过使用选项和将最小和最大堆大小设置为相同的值来避免延迟 -Xmx,并使用预接触所有内存-XX:+AlwaysPreTouch将这项工作移至 VM 启动阶段。
  • *特别是在 Linux 中,通过透明大页面 (THP)*功能将小页面合并为大页面往往会停止随机进程,而不仅仅是在暂停期间。由于 VM 分配和维护大量内存,因此 VM 将成为长时间停止的进程的风险比通常高。有关如何禁用透明大页面功能的信息,请参阅操作系统的文档。
  • 由于某些后台任务间歇性地占用了写入日志的硬盘的所有 I/O 带宽,写入日志输出可能会停止一段时间。考虑为您的日志或其他一些存储使用单独的磁盘,例如内存支持的文件系统来避免这种情况。

另一种需要注意的情况是实时比其他时间的总和大得多,这可能表明 VM 在可能过载的机器上没有获得足够的 CPU 时间。

7.3.3.2 引用对象处理时间过长

有关处理引用对象所用时间的信息显示在Reference Processing阶段中。在此Reference Processing阶段,G1 根据特定类型的引用对象的要求更新参考对象的所指对象。默认情况下,G1 尝试Reference Processing使用以下启发式方法并行化子阶段:对于每个-XX:ReferencesPerThread引用对象启动一个线程,由-XX:ParallelGCThreads. 可以通过设置-XX:ReferencesPerThread为 0 来禁用此启发式,以默认使用所有可用线程,或者完全禁用并行化-XX:-ParallelRefProcEnabled

7.3.3.3 Young-Only 阶段内的 Young-Only 收集耗时太长

正常的年轻代,一般来说,任何年轻代的收集所花费的时间大致与年轻代的大小成正比,或者更具体地说,是收集集中需要复制的活动对象的数量。如果Evacuate Collection Set阶段花费的时间太长,特别是Object Copy子阶段,请减少-XX:G1NewSizePercent。这减少了年轻代的最小大小,允许可能更短的暂停。

如果应用程序性能,特别是在垃圾收集中幸存的对象数量突然发生变化,则可能会出现另一个与年轻代大小有关的问题。这可能会导致垃圾收集暂停时间出现峰值。通过使用来减少最大年轻代大小可能很有用-XX:G1MaxNewSizePercent。这限制了年轻代的最大大小以及暂停期间需要处理的对象数量

7.3.3.4 Mixed Collections混合收集耗时太长

混合收集用于回收老年代的空间混合收集包含年轻代和老年代区域gc+ergo+cset=trace您可以通过启用日志输出来获取有关年轻代或老年代区域撤离对暂停时间的贡献的信息。分别查看年轻代区域和老年代区域的预测年轻区域时间和预测老区域时间。

如果预测的年轻区域时间太长,请参阅 7.3.3.3 Young-Only 阶段内的 Young-Only 收集耗时太长。否则,为了减少老年代区域对暂停时间的要求,G1 提供了三个选项:

  • 通过增加-XX:G1MixedGCCountTarget.
  • 通过使用 - XX:G1MixedGCLiveThresholdPercent 不将它们放入候选收集集合中,避免收集需要大量时间来收集的区域。在许多情况下,高度占用的区域需要花费大量时间来收集。
  • 尽早停止老年代空间回收,这样 G1 就不会收集那么多高占用区域。在这种情况下,增加 -XX:G1HeapWastePercent

请注意,最后两个选项减少了可以为当前空间回收阶段回收空间的收集集候选区域的数量。这可能意味着 G1 可能无法在老年代回收足够的空间来持续运行。但是,稍后的空间回收阶段可能能够对它们进行垃圾收集。

7.3.3.5 高更新 RS 和扫描 RS 时间

为了使 G1 能够撤出单个老年代区域,G1 跟踪跨区域引用的位置,即从一个区域指向另一个区域的引用。指向给定区域的跨区域引用集称为该区域的记忆集。移动区域的内容时必须更新记住的集合。区域的记忆集的维护大多是并发的。出于性能目的,当应用程序在两个对象之间安装新的跨区域引用时,G1 不会立即更新记住的区域集。记住的集合更新请求被延迟和批处理以提高效率。

G1 需要完整的记忆集进行垃圾收集,因此垃圾收集的更新 RS阶段会处理任何未完成的记忆集更新请求。扫描 RS阶段在记忆集中搜索对象引用,移动区域内容,然后将这些对象引用更新到新位置。根据应用程序,这两个阶段可能需要大量时间。

使用该选项调整堆区域的大小 -XX:G1HeapRegionSize会影响跨区域引用的数量以及记忆集的大小。处理区域的记忆集可能是垃圾收集工作的重要部分,因此这对可实现的最大暂停时间有直接影响。较大的区域往往具有较少的跨区域引用,因此处理它们所花费的相对工作量会减少,尽管与此同时,较大的区域可能意味着每个区域有更多的活动对象要撤离,从而增加了其他阶段的时间。

G1 尝试调度记忆集更新的并发处理,以便更新 RS 阶段大约占用-XX:G1RSetUpdatingPauseTimePercent允许的最大暂停时间的百分比。通过减小这个值,G1 通常会同时执行更多的记忆集更新工作。

虚假的高更新 RS 时间与分配大对象的应用程序相结合,可能是由试图通过批处理来减少并发记忆集更新工作的优化引起的。如果创建此类批处理的应用程序发生在垃圾回收之前,那么垃圾回收必须在暂停的更新 RS 时间部分处理所有这些工作。用于-XX:-ReduceInitialCardMarks禁用此行为并可能避免这些情况。

扫描 RS 时间还取决于 G1 执行的压缩量以保持记忆集存储大小较低。记忆集在内存中存储的越紧凑,在垃圾收集期间检索存储的值所需的时间就越多。G1 自动执行这种压缩,称为记忆集粗化,同时根据该区域记忆集的当前大小更新记忆集。特别是在最高压缩级别下,检索实际数据可能非常缓慢。-XX:G1SummarizeRSetStatsPeriod与级别日志记录相结合的选项 gc+remset=trace显示是否发生这种粗化。如果是这样,则在 GC 之前摘要X 部分的行Did <X> coarsenings中显示一个高值。这-XX:G1RSetRegionEntries可以显着增加选项以减少这些粗化的数量。避免在生产环境中使用这种详细的记忆集日志记录,因为收集这些数据可能需要大量时间。

7.3.4 调整吞吐量

G1 的默认策略试图在吞吐量和延迟之间保持平衡;但是,在某些情况下需要更高的吞吐量。除了如前几节所述减少整体暂停时间外,暂停的频率还可以减少。主要思想是通过使用来增加最大暂停时间-XX:MaxGCPauseMillis。代大小启发式会自动适应年轻代的大小,这直接决定了暂停的频率。如果这不会导致预期的行为,特别是在空间回收阶段,增加最小年轻代大小 -XX:G1NewSizePercent将迫使 G1 这样做。

在某些情况下,-XX:G1MaxNewSizePercent允许的最大年轻代大小可能会通过限制年轻代大小来限制吞吐量。这可以通过查看gc+heap=info日志的区域摘要输出来诊断。在这种情况下,Eden 区域和幸存者区域的总百分比接近区域-XX:G1MaxNewSizePercent总数的百分比。-XX:G1MaxNewSizePercent在这种情况下 考虑增加。

增加吞吐量的另一个选择是尝试减少并发工作量,特别是并发记忆集更新通常需要大量 CPU 资源。增加 -XX:G1RSetUpdatingPauseTimePercent将工作从并发操作转移到垃圾收集暂停。在最坏的情况下,可以通过设置禁用并发记忆集更新-XX:-G1UseAdaptiveConcRefinement -XX:G1ConcRefinementGreenZone=``2G -XX:G1ConcRefinementThreads=``0。这主要禁用此机制并将所有记住的集合更新工作移至下一个垃圾收集暂停。

通过 using 启用大页面的使用-XX:+UseLargePages也可以提高吞吐量。有关如何设置大页面的信息,请参阅您的操作系统文档。

您可以通过禁用它来最小化堆大小调整工作;将选项-Xms 和设置-Xmx 为相同的值。此外,您可以使用-XX:+AlwaysPreTouch将操作系统工作移到虚拟内存与物理内存到 VM 启动时间。为了使暂停时间更加一致,这两种措施都是特别可取的。

7.3.5 调整堆大小

与其他收集器一样,G1 旨在调整堆大小,以使垃圾收集所花费的时间低于由-XX:GCTimeRatio选项确定的比率。调整此选项以使 G1 满足您的要求。

7.3.6 可调默认值

本节介绍默认值和有关本主题中介绍的命令行选项的一些附加信息。

表 10-1 可调默认值 G1 GC

选项和默认值描述
-XX:+G1UseAdaptiveConcRefinement``-XX:G1ConcRefinementGreenZone=N-XX:G1ConcRefinementYellowZone=*N-XX:G1ConcRefinementRedZone=N-XX:G1ConcRefinementThreads=N并发记忆集更新(细化)使用这些选项来控制并发细化线程的工作分配。G1 为这些选项选择符合人体工程学的值,以便-XX:G1RSetUpdatingPauseTimePercent在垃圾收集暂停中花费时间来处理任何剩余的工作,并根据需要自适应地调整它们。请谨慎更改,因为这可能会导致非常长的停顿。
-XX:+ReduceInitialCardMarks这将用于初始对象分配的并发记忆集更新(细化)工作批处理在一起。
-XX:+ParallelRefProcEnabled``-XX:ReferencesPerThread=1000-XX:ReferencesPerThread确定并行化程度:对于每N个引用对象,一个线程将参与引用处理的子阶段,受-XX:ParallelGCThreads. -XX:ParallelGCThreads值 0 表示将始终使用 由值指示的最大线程数。这决定了是否处理 java.lang.Ref.*实例应该由多个线程并行完成。
-XX:G1RSetUpdatingPauseTimePercent=``10这决定了 G1 在更新 RS 阶段应该花费的总垃圾收集时间的百分比来更新任何剩余的记忆集。G1 使用此设置控制并发记忆集更新的数量。
-XX:G1SummarizeRSetStatsPeriod=``0这是 G1 生成记忆集摘要报告的许多 GC 中的时期。将此设置为零以禁用。生成记忆集摘要报告是一项代价高昂的操作,因此仅应在必要时使用,并且具有相当高的价值。用于 gc+remset=trace打印任何内容。
-XX:GCTimeRatio=``12这是应该花费在垃圾收集而不是应用程序上的目标时间比率的除数。确定在增加堆之前可用于垃圾收集的目标时间分数的实际公式是 1 / (1 + GCTimeRatio). 此默认值导致目标有大约 8% 的时间用于垃圾收集。

技术咨询支持,可以扫描微信公众号进行回复咨询
在这里插入图片描述

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋小生的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值