如何用Java处理较长的完整垃圾收集周期

时间:2019-05-01 15:28:07

标签: java garbage-collection jvm

我们继承了一个在生产环境中运行的系统,并且最近每10个小时就开始出现故障。基本上,如果一分钟内无响应,我们的内部软件会标记出发生故障的系统。我们发现我们的完整GC周期持续1.5分钟的问题,我们使用30 GB的堆。现在的问题是,我们无法在短时间内进行大量优化,也无法快速划分服务范围,但是由于生产中的这些停顿,我们需要尽快消​​除1.5分钟的停顿,因为我们的系统会出现故障。对于我们来说,可接受的延迟是20毫秒,但不能超过20毫秒。调整系统的最快方法是什么?减少堆以频繁触发GC?使用System.gc()提示?还有其他解决方案吗?我们使用Java 8的默认设置,并且用户越来越多-即创建了越来越多的对象。

某些GC统计信息

enter image description here

3 个答案:

答案 0 :(得分:2)

没有一个万能的万能解决方案可以解决您的问题:您需要很好地处理应用程序的分配和活动模式,并且需要了解它如何与特定应用程序交互您正在运行的垃圾回收算法(Java版本的功能和传递给java的命令行标志)。

从广义上讲,完整GC(成功回收大量空间)意味着很多对象都可以在次要集合中幸存(但不会泄漏)。首先查看您的Eden和Survivor空间的大小:如果Eden太小,次要收藏将非常频繁地运行,并且也许您没有给物体在达到保有权期限之前就死亡的机会。如果幸存者太小,则物体会过早地被提升到Old gen。

GC调整有点技巧:您运行应用程序,研究结果,调整一些参数,然后再次运行。因此,您将需要一个基准测试版本的应用程序,该版本的性能应与生产版本的性能尽可能接近,但希望它不需要10个小时即可产生完整的GC。

正如您所说的,您正在使用默认设置运行Java 8,我相信这意味着您的Old集合正在与Serial收集器一起运行。通过切换到旧版本的Parallel收集器(-XX:+ UseParallelOldGC),您可能会看到一些非常快速的改进。尽管这可能会将1.5分钟的暂停时间减少到几秒钟(取决于您的机器上的内核数以及为GC指定的线程数),但这不会将您的最大暂停时间减少到20ms。

答案 1 :(得分:2)

您有很多保留的数据。有一些值得考虑的选择。

  • 将堆增加到32 GB,如果您有可用内存,则影响不大。再次查看总数,您似乎正在使用32 GB而不是30 GB,因此这可能无济于事。
  • 如果您没有足够的可用内存,则可能会交换一小部分堆,因为这会大大增加完整的GC时间。
  • 可能有一些简单的方法可以使数据结构更紧凑。例如使用紧凑的字符串,使用原语而不是包装器,例如long作为时间戳,而不是DateLocalDateTime。 (long的大小约为1/8)
  • 如果以上两种方法均无帮助,请尝试将某些数据移出堆。例如Chronicle Map是ConcurrentMap,它使用堆外内存可以显着减少GC时间。即,无堆存储的数据没有GC开销。这种添加的难易程度在很大程度上取决于数据的结构。

我建议分析数据的结构,以查看是否有任何简便的方法可以使数据更有效。

答案 2 :(得分:0)

当我发生这种情况时,这是由于静态变量耗尽了内存而导致的内存泄漏。我将检查所有最近的代码更改,并查找任何可能的内存泄漏。