如何在Java中调整垃圾回收(翻译)

时间:2022-06-20
本文章向大家介绍如何在Java中调整垃圾回收(翻译),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

原文地址how-to-tune-garbage-collection-in-java 由于时间仓促,翻译中会出现很多错误,欢迎校正。

垃圾收集是JVM在不再需要内存时代表应用程序回收内存的机制。从高层来看,它包括查找不再使用的对象,释放与这些对象相关联的内存,偶尔压缩堆以防止内存碎片化。

垃圾收集器使用一个或多个线程执行它的工作。但是为了完成跟踪对象引用和在内存中移动对象的工作,需要确保应用程序线程当前没有使用这些对象,举个例子,如果应用程序线程正在使用对象,则对象的内存位置会由于GC而发生更改,这是更坏和不可预测的事情。这就是垃圾收集器在执行某些任务时必须暂停所有应用程序线程的原因。这种暂停有时候被称作 Stop-The-Wolrd(STW)暂停,最小化它们是GC调整的首要关注点,因为它们对Java应用程序的性能会产生巨大影响。

堆的大小

垃圾收集优化的第一步是调整堆的大小。这是因为如果堆太小的话,会出现太多的GC来回收内存,这会降低应用程序的总吞吐量。如果堆太大,那么会有更少的GC,但是这些GC需要花费很长时间,响应时间指标会受到影响。并行收集器特别容易受到此问题的影响,因此如果需要大堆和低暂停时间,则应尝试使用G1GC收集器。

备注: 自Java 9以来,Concurrent Mark Sweep(CMS)收集器已被弃用, Shenandoah Garbage Collector垃圾收集器在撰写本文时仍被认为是“实验性的”。因此,如果您运行的是联机交互式应用程序,那么G1GC应该是您的默认选择,如果您运行的是脱机批处理应用程序,那么并行收集器应该是您的第一选择。

<!-- more -->

堆的大小由两个值控制:用ms参数指定的初始值和用mx参数指定的最大值

例如

-Xms1g -Xmx8g

拥有堆的初始和最大大小允许JVM是根据工作负载自动调整堆大小。如果JVM遇到内存压力,并且发现它执行了太多的GC,它将不断增加堆,直到内存压力降低,或者直到堆达到其最大大小。如果内存压力很低,JVM还可以通过缩小堆大小来决定减少暂停时间。这个过程称为自适应大小调整,它不仅调整堆的总体大小,而且调整年轻代和老年代的大小和比率。

如果您花了一些时间来微调应用程序的GC行为和大小,您可以选择关闭自适应大小调整。这可以节省JVM,即计算堆大小所需的一小段时间。您可以通过将参数useAdaptiveSizePolicy设置为false来完成此操作。

例如:

-XX:-UseAdaptiveSizePolicy

另外,将初始堆大小设置为与最大堆大小相同的值,或将初始新生代大小设置为与最大新生代大小相同的值,可以有效地关闭部分自适应大小调整行为。

强烈建议设置最大堆大小的准则是:最大堆大小不应超过计算机上的物理内存量。如果有多个JVM在运行,那么最大堆大小之和不应该超过机器的物理内存。

设置最大堆大小的一个更一般的建议是,应该设置该大小,以便在完全GC之后堆占满30%。要计算这个值,您可以在GC日志中查找发生完整GC的条目,并观察GC完成时使用了多少内存。或者,您可以运行应用程序,直到它达到稳定状态,然后使用jconsolejcmd强制执行完整的GC。

GC性能调优

如果启用了自适应大小调整,则可以使用MaxGCPauseMillis参数来调整GC行为。此标志为最大GC暂停时间设置目标。当与并行收集器一起使用时,JVM将调整年轻一代和老年一代的大小,以尝试实现目标。然后,它将调整堆的大小,以便在GC中花费的时间不超过某个值,默认情况下,该值为1%。

G1GC的目标之一是它将需要最小的调优。因此,在G1GC中,一个调优参数maxgcpausemillis执行以下所有优化,以尝试实现指定的暂停时间目标:

  • 调整堆的大小,
  • 尽快开始后台处理,
  • 调整要提升到老年代的对象的寿命阈值,
  • 调整在混合GC循环期间处理的老年代数。 在G1GC中,参数的默认值是200ms,虽然您可能会尝试将其设置为非常小的值,如20 ms,但请注意,为了实现此目的,垃圾收集器将把新生代缩小到非常小的大小,并收集较少的老年代,这最终会导致出现垃圾太多的情况。老年代和系统必须执行完整的GC,这是不可取的。

修复并发模式故障

G1GC是一个并发收集器。这意味着垃圾收集进程的某些阶段可以在应用程序线程仍在运行时并发运行。由于正在运行的应用程序可以继续产生垃圾,因此我们可能会遇到这样的情况:当垃圾收集器仍处于垃圾收集过程的中间时,应用程序将耗尽老年代内存。换句话说,正在运行的应用程序产生垃圾的速度比可以清除垃圾的速度要快。这种情况被称为并发模式故障,提升故障或疏散故障,具体取决于故障发生的时间。如果您在GC日志中看到很多这些错误,那么解决方案是要么增加堆的大小,更早地启动G1后台处理,要么通过使用更多的后台线程来加速GC处理。

要更频繁地执行g1后台活动,可以降低触发G1周期的阈值。这是通过减少InitiatingEapOccupancyPercent参数值来实现的。

-XX:InitiatingHeapOccupancyPercent=45

默认情况下,这个参数设置为45。这意味着当堆填充45%时,将触发GC循环。减小这个值意味着GC将更早更频繁地被触发。但应注意的是,该值不能设置为太低的数字,这将导致GCS发生的频率太高。

要增加后台线程的数量,请使用concgthreads参数。

-xx:concgthreads=4个

这个参数的默认值设置为ParallelGCThreads的值加上2,再除以4。只要机器上有足够的CPU可用,就可以增加该值,而不会造成任何性能损失。

如果调优堆大小和调优收集器对您不起作用,那么您可以尝试另一个收集器。如果您仍然没有得到好的结果,那么您需要考虑调整应用程序代码本身。

愉快的调优吧!