JVM调优实战:G1中的to-space exhausted问题

我最近将我的一个应用程序从CMS升级到了G1。一天早上,只是坐在办公室里,我接到一个电话,然后去检查监控。我发现机器的记忆力增加了90度,如下图所示。显示:

Image.png

查看GC日志后,我在该时间点附近找到了“空间耗尽”的日志。 (关于G1日志学习,请参阅我之前的文章:[翻译]深入理解G1的GC日志(1))p>

Image.png

在这里,我感到惊讶的是,啥to-sapce的耗尽将导致整个机器的内存激增。我们的JVM团队同学向我解释说旧区是不够的。此时,年轻区中的所有对象都将被转换为旧区对象而不管生死,因此总内存使用量将急剧增加。这个知识点,我在学习G1时没有得到它(关于G1的基础知识,请参阅上一篇文章:可能是最全面的G1学习笔记)。

但是,我有另一个问题:如果xmx和xms相同,则堆空间应该相同。如果你从头开始分配5g然后添加非堆内存,那么java进程将超过5g,这很好;但是这里使用空闲内存也应该利用堆上的空间,然后应该分配整个内存块,机器内存不应该激增。 JVM团队的同学向我解释说:不,我第一次读或写时,实际的物理内存将从操作系统中分配。

针对上述问题,我们最终确定了以下调整建议:

这次没有FGC,可能是因为我之前已经将xmx和xms调高了。这次我要将xmx和xms调整回原来的值;

使用HeapDumpAfterFullGC参数,下次出现类似情况时,将触发FGC,然后自动转储堆内存,以便可以分析堆内存以查看哪些对象占用了如此多的内存,然后它就可以了优化。

基于上述问题,我去找了一些信息,并按如下方式组织。

如本书第123页所述,上述情况属于促销失败的情况。 G1收集器完成标记阶段,启动混合垃圾收集,准备清理旧的分区,但旧的分区在垃圾收集中。设备在释放足够空间之前已耗尽。这种失败通常意味着混合垃圾收集需要更快地完成垃圾收集,并且每一代新垃圾收集都需要处理更多的旧年分区。一般来说,FGC会追随一系列耗尽的空间。

在上面的示例中,旧区域的使用比垃圾收集器的恢复速度快,因此请考虑两个调整思路

让G1更早地开始混合垃圾收集循环。通过调整此参数,默认情况下参数为45(PS:此参数表示整个堆内存的比例),但此参数也无法调整。很小,否则会导致过多的并发收集周期和混合垃圾收集,给应用程序带来太多停顿。

除了考虑增加速度之外,您还可以考虑增加每个混合垃圾收集所收集的旧分区的数量。通过调整参数,您可以控制在每个混合循环中恢复的旧分区数。此参数的默认值为8;

请特别注意日志片段中的两个日志'to-space exhausted'和'Evacuation Failure',如下图所示。可以看出,疏散失败消耗684.1ms,这意味着传输失败导致应用程序暂停近1秒。

Image.png

这种情况是转移失败。本书提出了两点建议:

与《Java性能权威指南》一样,还建议减少此参数的值,因为传输失败的成本远高于某些并发标记周期的执行

建议调整用于垃圾收集的线程数,代价是CPU消耗更多;也就是说,它将占用Java应用程序的CPU时间,这也需要进行权衡。

有时传输失败,因为幸存者分区中没有足够的空间来容纳新提升的对象。如果是这种情况,请考虑增加尺寸。在G1中,默认值为10%。

JVM参数的调整是一个连续推导和试验的过程。最重要的数据是GC日志和Java堆内存快照。因此:(1)在JVM参数中设置HeapDumpAfterFullGC和HeapDumpOnOutOfMemoryError的参数。发送FGC和OOM时,记录当前Java堆状态以进行事后分析; (2)GC日志应单独打印到日志文件中以便于分析。如果未设置,GC日志将打印到stdout.log。在中间,将在中间混合其他日志以影响故障排除。

JVM的参数调整不是灵丹妙药。当OOM或FGC发生时,业务代码中必须存在不合理的位置。有必要进行合理的限制和优化,并不是所有的东西都可以提供给JVM。

本文重点介绍后端技术,JVM故障排除和优化,Java面试问题,个人成长和自我管理主题,为读者提供一线开发人员的工作和成长经验,希望您能在这里获得一些成果。

Javaadu

Javaadu

0.2

2019.08.24 21: 17 *

字数1515

我最近将我的一个应用程序从CMS升级到了G1。一天早上,只是坐在办公室里,我接到一个电话,然后去检查监控。我发现机器的记忆力增加了90度,如下图所示。显示:

Image.png

查看GC日志后,我在该时间点附近找到了“空间耗尽”的日志。 (关于G1日志学习,请参阅我之前的文章:[翻译]深入理解G1的GC日志(1))p>

Image.png

在这里,我感到惊讶的是,啥to-sapce的耗尽将导致整个机器的内存激增。我们的JVM团队同学向我解释说旧区是不够的。此时,年轻区中的所有对象都将被转换为旧区对象而不管生死,因此总内存使用量将急剧增加。这个知识点,我在学习G1时没有得到它(关于G1的基础知识,请参阅上一篇文章:可能是最全面的G1学习笔记)。

但是,我有另一个问题:如果xmx和xms相同,则堆空间应该相同。如果你从头开始分配5g然后添加非堆内存,那么java进程将超过5g,这很好;但是这里使用空闲内存也应该利用堆上的空间,然后应该分配整个内存块,机器内存不应该激增。 JVM团队的同学向我解释说:不,我第一次读或写时,实际的物理内存将从操作系统中分配。

针对上述问题,我们最终确定了以下调整建议:

这次没有FGC,可能是因为我之前已经将xmx和xms调高了。这次我要将xmx和xms调整回原来的值;

使用HeapDumpAfterFullGC参数,下次出现类似情况时,将触发FGC,然后自动转储堆内存,以便可以分析堆内存以查看哪些对象占用了如此多的内存,然后它就可以了优化。

基于上述问题,我去找了一些信息,并按如下方式组织。

如本书第123页所述,上述情况属于促销失败的情况。 G1收集器完成标记阶段,启动混合垃圾收集,准备清理旧的分区,但旧的分区在垃圾收集中。设备在释放足够空间之前已耗尽。这种失败通常意味着混合垃圾收集需要更快地完成垃圾收集,并且每一代新垃圾收集都需要处理更多的旧年分区。一般来说,FGC会追随一系列耗尽的空间。

在上面的示例中,旧区域的使用比垃圾收集器的恢复速度快,因此请考虑两个调整思路

让G1更早地开始混合垃圾收集循环。通过调整此参数,默认情况下参数为45(PS:此参数表示整个堆内存的比例),但此参数也无法调整。很小,否则会导致过多的并发收集周期和混合垃圾收集,给应用程序带来太多停顿。

除了考虑增加速度之外,您还可以考虑增加每个混合垃圾收集所收集的旧分区的数量。通过调整参数,您可以控制在每个混合循环中恢复的旧分区数。此参数的默认值为8;

请特别注意日志片段中的两个日志'to-space exhausted'和'Evacuation Failure',如下图所示。可以看出,疏散失败消耗684.1ms,这意味着传输失败导致应用程序暂停近1秒。

Image.png

这种情况是转移失败。本书提出了两点建议:

与《Java性能权威指南》一样,还建议减少此参数的值,因为传输失败的成本远高于某些并发标记周期的执行

建议调整用于垃圾收集的线程数,代价是CPU消耗更多;也就是说,它将占用Java应用程序的CPU时间,这也需要进行权衡。

有时传输失败,因为幸存者分区中没有足够的空间来容纳新提升的对象。如果是这种情况,请考虑增加尺寸。在G1中,默认值为10%。

JVM参数的调整是一个连续推导和试验的过程。最重要的数据是GC日志和Java堆内存快照。因此:(1)在JVM参数中设置HeapDumpAfterFullGC和HeapDumpOnOutOfMemoryError的参数。发送FGC和OOM时,记录当前Java堆状态以进行事后分析; (2)GC日志应单独打印到日志文件中以便于分析。如果未设置,GC日志将打印到stdout.log。在中间,将在中间混合其他日志以影响故障排除。

JVM的参数调整不是灵丹妙药。当OOM或FGC发生时,业务代码中必须存在不合理的位置。有必要进行合理的限制和优化,并不是所有的东西都可以提供给JVM。

本文重点介绍后端技术,JVM故障排除和优化,Java面试问题,个人成长和自我管理主题,为读者提供一线开发人员的工作和成长经验,希望您能在这里获得一些成果。

Javaadu

我最近将我的一个应用程序从CMS升级到了G1。一天早上,只是坐在办公室里,我接到一个电话,然后去检查监控。我发现机器的记忆力增加了90度,如下图所示。显示:

Image.png

查看GC日志后,我在该时间点附近找到了“空间耗尽”的日志。 (关于G1日志学习,请参阅我之前的文章:[翻译]深入理解G1的GC日志(1))p>

Image.png

在这里,我感到惊讶的是,啥to-sapce的耗尽将导致整个机器的内存激增。我们的JVM团队同学向我解释说旧区是不够的。此时,年轻区中的所有对象都将被转换为旧区对象而不管生死,因此总内存使用量将急剧增加。这个知识点,我在学习G1时没有得到它(关于G1的基础知识,请参阅上一篇文章:可能是最全面的G1学习笔记)。

但是,我有另一个问题:如果xmx和xms相同,则堆空间应该相同。如果你从头开始分配5g然后添加非堆内存,那么java进程将超过5g,这很好;但是这里使用空闲内存也应该利用堆上的空间,然后应该分配整个内存块,机器内存不应该激增。 JVM团队的同学向我解释说:不,我第一次读或写时,实际的物理内存将从操作系统中分配。

针对上述问题,我们最终确定了以下调整建议:

这次没有FGC,可能是因为我之前已经将xmx和xms调高了。这次我要将xmx和xms调整回原来的值;

使用HeapDumpAfterFullGC参数,下次出现类似情况时,将触发FGC,然后自动转储堆内存,以便可以分析堆内存以查看哪些对象占用了如此多的内存,然后它就可以了优化。

基于上述问题,我去找了一些信息,并按如下方式组织。

如本书第123页所述,上述情况属于促销失败的情况。 G1收集器完成标记阶段,启动混合垃圾收集,准备清理旧的分区,但旧的分区在垃圾收集中。设备在释放足够空间之前已耗尽。这种失败通常意味着混合垃圾收集需要更快地完成垃圾收集,并且每一代新垃圾收集都需要处理更多的旧年分区。一般来说,FGC会追随一系列耗尽的空间。

在上面的示例中,旧区域的使用比垃圾收集器的恢复速度快,因此请考虑两个调整思路

让G1更早地开始混合垃圾收集循环。通过调整此参数,默认情况下参数为45(PS:此参数表示整个堆内存的比例),但此参数也无法调整。很小,否则会导致过多的并发收集周期和混合垃圾收集,给应用程序带来太多停顿。

除了考虑增加速度之外,您还可以考虑增加每个混合垃圾收集所收集的旧分区的数量。通过调整参数,您可以控制在每个混合循环中恢复的旧分区数。此参数的默认值为8;

请特别注意日志片段中的两个日志'to-space exhausted'和'Evacuation Failure',如下图所示。可以看出,疏散失败消耗684.1ms,这意味着传输失败导致应用程序暂停近1秒。

Image.png

这种情况是转移失败。本书提出了两点建议:

与《Java性能权威指南》一样,还建议减少此参数的值,因为传输失败的成本远高于某些并发标记周期的执行

建议调整用于垃圾收集的线程数,代价是CPU消耗更多;也就是说,它将占用Java应用程序的CPU时间,这也需要进行权衡。

有时传输失败,因为幸存者分区中没有足够的空间来容纳新提升的对象。如果是这种情况,请考虑增加尺寸。在G1中,默认值为10%。

JVM参数的调整是一个连续推导和试验的过程。最重要的数据是GC日志和Java堆内存快照。因此:(1)在JVM参数中设置HeapDumpAfterFullGC和HeapDumpOnOutOfMemoryError的参数。发送FGC和OOM时,记录当前Java堆状态以进行事后分析; (2)GC日志应单独打印到日志文件中以便于分析。如果未设置,GC日志将打印到stdout.log。在中间,将在中间混合其他日志以影响故障排除。

JVM的参数调整并不是万能的。当OOM或FGC发生时,业务代码中必须有不合理的地方。有必要进行合理的限制和优化,并不是所有的事情都可以提供给JVM。

本文着重介绍了后端技术、JVM故障排除与优化、Java面试问题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,希望您能在这里有所收获。

0×251f

爪哇岛

——