本文主要是介绍06_G1调优配置,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本章主要介绍,如果G1默认的一些配置无法满足你的需求,要如何进一步调优。
G1的一般建议
一般建议是使用G1并保持默认设置,如有需要,可以通过使用 -Xmx 来设置最大的Java堆大小,同时也可以通过 -XX:MaxGCPauseMillis来指定不同的暂停时间目标。
G1的默认配置与其他收集器的平衡有所不同。G1在默认配置中的目标既不是追求最大吞吐量,也不是追求最低延迟,而是提供相对较小、统一的暂停时间以获得高吞吐量。然而,G1在堆空间中逐步回收空间和暂停时间控制会给应用线程和空间回收效率带来一些开销。
如果您更看重高吞吐量,则可以通过使用 -XX:MaxGCPauseMillis 放宽暂停时间目标或提供更大的堆。如果延迟是主要考虑因素,则可以修改暂停时间目标。应尽量避免使用类似 -Xmn、-XX:NewRatio 等选项限制年轻代大小,因为年轻代大小是G1控制暂停时间的主要手段。将年轻代大小设置为一个固定值会覆盖并实际上禁用了暂停时间控制。
从其他收集器转向 G1
通常情况下,从其他收集器(尤其是并发标记清除收集器)切换到G1时,首先应删除所有影响垃圾收集的选项,只通过 -Xmx 以及可选的 -Xms 来设置暂停时间目标和总体堆大小。
许多对其他收集器有效的选项在G1中要么根本不起作用,要么甚至会降低吞吐量和满足暂停时间目标的可能性。一个例子就是设置年轻代大小,这样完全阻止了G1调整年轻代大小来满足暂停时间目标。
改善 G1的性能表现
G1旨在在无需指定额外选项的情况下提供良好的整体性能。然而,在某些情况下,默认的启发式算法或其配置可能会导致性能不佳。本节提供了一些诊断和改进这些情况的指南。本指南仅描述了针对给定应用程序,在给定度量标准下,G1提供了哪些可能性来改进垃圾收集器的性能。从案例出发,应用级别的优化可能比尝试调整虚拟机以获得更好性能更为有效,例如通过完全避免一些长生命周期对象所带来的一些问题情况。
为了诊断目的,G1提供了全面的日志记录。一个很好的起点是使用 -Xlog:gc*=debug 选项,然后根据需要细化输出内容。日志提供了关于垃圾收集活动在暂停期间和暂停期间之外的详细概述。其中包括收集类型以及在暂停期间特定阶段所花费的时间。
接下来的小节将探讨一些常见性能问题。
观察FGC
Full GC 通常会耗费大量时间。由于老年代堆内存占用过高而引起的Full GC,在日志中可以通过找到“Pause Full (G1 Compaction Pause)”这些词来检测。而Full GC通常都是由于垃圾回收遭遇了疏散失败(Evacuation Failure)标签表示的先前事件导致。
Full GC发生的原因是因为应用程序分配了太多无法迅速回收的对象。通常情况下,并发标记无法及时完成以启动空间回收阶段。频繁分配大对象可能加剧出现Full GC的概率。由于G1中这些对象的分配方式,它们可能会占用比预期更多的内存。
目标应该是确保并发标记能够按时完成。这可以通过减少老年代中的分配速率,或者给并发标记更多时间来实现。
G1提供了一些选项来更好地处理这种情况:
- - 可以使用gc+heap=info日志记录来确定Java堆上被巨型对象占用的区域数量。在“Humongous regions:X->Y”这一行中,Y表示被巨型对象占用的区域数量。如果与老年代区域数量相比,这个数字较高,则最佳选项是尝试减少这些对象的数量。可以通过增加区域大小来实现,使用 -XX:G1HeapRegionSize 选项设定区域大小。当前所选的堆区域大小将在日志开头打印出来。
- - 增加Java堆的大小。通常会增加标记过程完成所需时间。
- - 通过显式设置 -XX:ConcGCThreads 来增加并发标记线程数。
- - 强制G1提早开始标记阶段。G1根据先前应用程序行为自动确定初始堆占用百分比(IHOP)阈值。如果应用程序行为发生变化,这些预测可能有误。有两种选择:增加自适应IHOP计算中使用的缓冲区以降低开始空间回收的目标占用率,即修改 -XX:G1ReservePercent;或者通过手动设置 -XX:-G1UseAdaptiveIHOP 和 -XX:InitiatingHeapOccupancyPercent 来禁用IHOP自适应计算。
Full GC除了分配失效之外还有其他原因,通常表明应用程序或某些外部工具导致进行了完整堆收集。如果造成原因是System.gc(),且无法修改应用程序源代码,则可以通过使用 -XX:+ExplicitGCInvokesConcurrent 来缓解Full GC带来的影响,或者通过设置 -XX:+DisableExplicitGC 让虚拟机完全忽略它们;外部工具可能仍然会强制进行Full GC,只能通过不再请求它们来移除这些影响。
巨大对象的碎片
完整的垃圾回收(Full GC)有可能在Java堆内存耗尽之前发生,因为需要找到一组连续的区域。在这种情况下,可以考虑增加堆区域大小,通过使用选项 -XX:G1HeapRegionSize 来减少巨型对象的数量,或者增加堆的大小。在极端情况下,即使可用内存表明有足够的内存空间,G1可能无法为对象分配到足够的连续空间。如果这种情况发生,那么在Full GC无法回收足够的连续空间时会导致虚拟机退出。因此,在这种情况下,除了减少如前所述巨型对象分配的数量或增加堆之外别无选择。
为延迟调优
这部分讨论了改善G1行为的提示,以解决常见的延迟问题,即暂停时间过长的情况。
非常规系统或实时使用
对于每次垃圾回收暂停,gc+cpu=info日志输出中都包含一行,其中包括来自操作系统的信息,详细说明了在暂停期间花费的时间。例如,输出中的一行可能是:User=0.19s Sys=0.00s Real=0.01s。
用户时间是在虚拟机代码中花费的时间,系统时间是在操作系统中花费的时间,而实际时间则是在暂停期间经过的绝对时间量。如果系统时间相对较高,则大多数情况下环境是造成延迟的原因。
导致系统时间过高的常见已知问题包括:
- - 虚拟机分配或返还操作系统内存可能会导致不必要的延迟。通过使用选项 -Xms 和 -Xmx 将最小堆大小和最大堆大小设置为相同值,并使用 -XX:+AlwaysPreTouch 预分配所有内存,将这一工作移到虚拟机启动阶段以避免这些延迟。
- - 特别是在Linux环境下,透明大页(THP)功能将小页面合并为大页面,往往会使随机进程停滞,而不仅仅只是在暂停期间。由于虚拟机分配和维护了大量内存,因此出现虚拟机长时间停滞的风险比通常更高。请参阅您操作系统的文档以了解如何禁用透明大页功能。
- - 由于某些后台任务不时占用写入日志所用硬盘的所有I/O带宽,日志输出可能会暂时停顿一段时间。考虑使用独立硬盘或其他存储设备来存储日志文件,例如基于内存的文件系统,从而避免这种情况。
另一个需要注意的情况是实际时间远远大于其他两者之和,这可能表明虚拟机未能获得足够CPU时间,在可能过载的计算机上发生了长时间停滞。
引用对象处理花费的时间太长
在“引用处理”阶段会显示关于引用对象处理时间的信息。在引用处理阶段,G1根据特定类型的引用对象的需求更新引用对象的引用。默认情况下,G1尝试使用以下启发式方法并行化引用处理的子阶段:对于每个 -XX:ReferencesPerThread 个引用对象,启动一个线程,并受 -XX:ParallelGCThreads 指定线程数的限制。可以通过将 -XX:ReferencesPerThread 设置为 0 来禁用此启发式方法,默认情况下使用所有可用线程,或通过 -XX:-ParallelRefProcEnabled 完全禁用并行化。
年轻代收集耗费太多时间
年轻代的普通收集以及一般情况下任何年轻代收集的时间大致与年轻代的大小成正比,或者更具体地说,与需要被复制的收集集合中的存活对象数量成正比。如果疏散集合阶段花费的时间过长,特别是对象复制子阶段,则可以通过减小 -XX:G1NewSizePercent 来解决。这会减小年轻代的最小大小,有望缩短暂停时间。
另一个关于年轻代大小的问题可能出现在应用程序性能以及尤其是在收集后存活对象数量突然改变时。这可能导致垃圾回收暂停时间出现波动。通过使用 -XX:G1MaxNewSizePercent 减小最大年轻代大小可能会有所帮助。这样可以限制年轻代的最大大小,从而减少暂停期间需要处理的对象数量。
混合收集花费的时间太长
混合年轻代收集用于回收老年代的空间。混合收集的收集集合包含年轻代和老年代区域。您可以通过启用 gc+ergo+cset=debug 日志输出来获取有关疏散年轻代或老年代区域对暂停时间贡献的信息。寻找以下日志消息:
Added young regions to CSet.[...] predicted eden time:4.86ms,predicted base time:9.98ms,target pause time:200.00ms,[...]
Eden 时间和 base 时间一起给出了预测的年轻代区域时间,即 G1 预计疏散年轻代所需的时间。
预测旧区域时间的日志消息如下所示:
Finish choosing collection set old regions.[...] predicted initial time:147.70ms,predicted optional time:15.45ms,[...]
在这里,预测初始时间表示预测的旧区域时间,即 G1 预计疏散最小集合的老年代区域所需的时间。
如果预测的年轻代区域时间太长,请参阅“只有年轻代收集在年轻代阶段花费太长时间”部分进行调整。否则,为了减少老年代区域对暂停时间的贡献,G1 提供了三个选项:
- 通过增加 -XX:G1MixedGCCountTarget 来将老年代区域回收分布到更多垃圾回收中。
- 通过使用 -XX:G1MixedGCLiveThresholdPercent 将需要大量时间来回收的区域排除在候选回收集合之外,以避免将它们放入候选回收集合。在许多情况下,占用率高的区域需要大量时间来进行回收。
- 提早停止老年代空间回收,这样 G1 将不会回收那么多占用率高的区域。在这种情况下,增加 -XX:G1HeapWastePercent。
请注意,最后两个选项会减少候选回收集合中可进行当前空间回收阶段空间回收数量。这可能意味着 G1 可能无法为持续操作回收足够老年代空间。但是,稍后的空间回收阶段可能会进行垃圾回首处理这些空间。
连续出现垃圾回收
G1默认的MMU设置允许连续进行垃圾回收。-XX:GCPauseIntervalMillis 的默认值略高于 -XX:MaxGCPauseMillis。如果观察到连续进行垃圾回收导致应用无法进展,可以增加 -XX:GCPauseIntervalMillis 的值至一个可接受的水平。这样,G1 将尝试减少垃圾回收的频率。
高合并堆根和扫描堆根时间
减少这两个阶段的一种方法是通过调整组合记忆集中记忆集条目的数量来减少。使用 -XX:G1HeapRegionSize 选项调整堆区域的大小可以减少跨区域引用大小的记忆集。更大的区域往往具有较少的跨区域引用,因此在处理它们时所花费的工作量减少,尽管与此同时,更大的区域可能意味着每个区域需要疏散更多存活对象,从而增加其他阶段的时间。
如果垃圾回收过程中有相当多的时间(即超过60%)花费在这两个阶段上,一个选择是通过减小 -XX:GCCardSizeInBytes 选项的值来降低记忆集条目的粒度:更细粒度会减少查找引用所需的工作量,但会增加一些额外内存消耗。
在应用程序分配大对象时,结合出现无端高 Scan Heap Roots 时间可能是由于一种优化引起,该优化尝试通过批量处理来减少并发记忆集更新工作。如果此类批处理是在垃圾回收之前创建的应用程序执行,则可能对 Merge Heap Roots 时间产生负面影响。使用 -XX:-ReduceInitialCardMarks 可以禁用此优化,从而潜在避免出现这种情况。
为吞吐量调优
G1的默认策略试图在吞吐量和延迟之间取得平衡;然而,在某些情况下,更高的吞吐量是可取的。除了按照前面部分所描述的减少总暂停时间之外,还可以减少暂停的频率。主要思路是使用 -XX:MaxGCPauseMillis 增加最大暂停时间。代际大小的启发式方式将自动调整年轻代的大小,这直接决定了暂停的频率。如果这不会产生预期行为,尤其是在空间回收阶段,可以使用 -XX:G1NewSizePercent 来增加最小年轻代大小来强制 G1 这样做。
在某些情况下,-XX:G1MaxNewSizePercent,即允许的年轻代最大大小,可能会通过限制年轻代大小来限制吞吐量。可以通过查看gc+heap=info日志记录中区域汇总输出来进行诊断。在这种情况下,Eden 区域和 Survivor 区域的合并百分比接近于总区域数的 -XX:G1MaxNewSizePercent 百分比。此时可以考虑增加 -XX:G1MaxNewSizePercent。
另一个提高吞吐量的选择是减少并发工作量。特别是,并发记忆集更新通常需要大量 CPU 资源。可以使用 -XX:G1RSetUpdatingPauseTimePercent 选项将工作从并发操作转移到垃圾回收暂停中。
增加此值可能会减少同时调度给应用程序进行细化工作,反之亦然;减少此值可能会增加同时与应用程序进行细化工作的量。
启用 gc+phases=debug 日志记录时,在 Merge Heap Roots 阶段的 Log Buffers 部分跟踪垃圾回收暂停中的细化工作。
通过使用 -XX:+UseLargePages 启用大页功能也可能提高吞吐量。请参考操作系统文档以了解如何设置大页。
您可以通过禁用堆调整工作来尽量减少它;将选项 -Xms 和 -Xmx 设置为相同值。此外,您还可以使用 -XX:+AlwaysPreTouch 将操作系统工作移到虚拟内存上以缩短虚拟机启动时间。这些措施特别有助于使暂停时间更加稳定。
为堆大小调优
与其他收集器一样,G1旨在调整堆大小,使得垃圾回收所花费的时间低于由 -XX:GCTimeRatio 选项确定的比率。调整此选项以使G1满足您的需求。
可调的默认值
本节介绍本主题中引入的默认值和有关命令行选项的一些描述和附加信息。
Option and Default Value | Description |
---|---|
| This batches together concurrent remembered set update (refinement) work for initial object allocations. |
|
This determines whether processing of java.lang.Ref.* instances should be done in parallel by multiple threads. |
| The concurrent remembered set update (refinement) work can be controlled with this option. Refinement tries to schedule work concurrently so that at most |
| This is the period in a number of GCs that G1 generates remembered set summary reports. Set this to zero to disable. Generating remembered set summary reports is a costly operation, so it should be used only if necessary, and with a reasonably high value. Use |
| This is the divisor for the target ratio of time that should be spent in garbage collection as opposed to the application. The actual formula for determining the target fraction of time that can be spent in garbage collection before increasing the heap is |
-XX:G1PeriodicGCInterval= 0 | The interval in ms to check whether G1 should trigger a periodic garbage collection. Set to zero to disable. |
-XX:+G1PeriodicGCInvokesConcurrent | If set, periodic garbage collections trigger a concurrent marking or continue the existing collection cycle, otherwise trigger a Full GC. |
-XX:G1PeriodicGCSystemLoadThreshold= 0.0 | Threshold for the current system load as returned by the hosts getloadavg() call to determine whether a periodic garbage collection should be triggered. A current system load higher than this value prevents periodic garbage collections. A value of zero indicates that this threshold check is disabled. |
注意: < ergo > 意味着实际值是根据环境的人机工程学确定的。
这篇关于06_G1调优配置的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!