Java垃圾收集引起的延迟影响性能



我注意到我的应用程序中有一个奇怪的现象。在将对象提交给服务器后,我将它们存储在Hashmap中,并在响应到达时删除它们。

我最近注意到运行后非常慢的性能。经过检查,我发现内存使用保持在4GB,然后下降到不到1gb。我怀疑它清理了很多对象,这就是性能变得如此糟糕的原因。

所以问题是为什么Java这么晚才开始垃圾收集?也就是说,为什么要等到堆满了再进行垃圾收集呢?它不应该定期收集垃圾吗?

存储在HashMap中的对象是在那个时候创建的,也就是说它们的寿命不长。

这是在Linux (RHEL), Oracle JVM HotSpot 7上。64位。4核。应用程序是这样运行的:

java -jar -Xmx4g prog.jar

注意:我已经看到了这个:调优垃圾收集以获得低延迟,但是现在我想了解为什么GC需要这么长时间才能启动?

听起来你有两个问题:

  1. 如果你有半长寿命的对象,它们将从年轻代移动到更老的代(比如在热点上移动到标记压缩代)。也就是说,它们将在收集时停止引用计数,并开始为较慢的GC保留。
  2. 你的年轻代堆空间太小,你的使用模式迫使对象从年轻代移动到老代。

我会考虑把你的年轻一代调整得更大。请参阅分代垃圾收集,了解不同类型的代。

所以问题是为什么Java这么晚才开始垃圾收集?也就是说,为什么要等到堆满了再进行垃圾收集呢?它不应该定期收集垃圾吗?

您正在使用"吞吐量"垃圾收集器,这就是该收集器的设计行为。它旨在通过最小化用于垃圾收集的CPU时间百分比来最大化系统吞吐量。它通过等待直到堆(或者更准确地说,新对象空间)满的简单策略来实现这一点。在所有条件相同的情况下,最有效的方法是:

  • 收集垃圾,当你有很多垃圾,和
  • 在垃圾收集时停止所有其他操作。

(要理解原因,您需要了解复制收集器如何工作的技术细节…)

当然,这意味着你会有大量的停顿。

如果您想要低延迟,则需要使用不同的收集器。然而,这导致在垃圾收集上花费的实际CPU时间的百分比更大……和其他gc相关的开销。


如果你有很多重要的停顿,那么也可能有一个相对大小的空间的问题。但是,在您摆弄相关参数之前,建议您打开GC日志记录,以尝试处理导致暂停的原因以及它们的频率。

这仅仅是因为默认的GC不会启动,除非堆已满。

JVM选择的默认垃圾收集器是Parallel GC。它的目标是在尽可能短的"停止世界"暂停期间释放尽可能多的内存。除非伊甸园满了,否则它不会在年轻一代开始。同样,它也不会从老一代开始,除非Tenured满了。

如果您想定期清理内存,可以切换到CMS。简单地使用标志-XX:+UseConcMarkSweepGC。但是,它会给应用程序带来一些开销。这是在不暂停应用程序的情况下定期运行GC的成本。

所以,总结一下:

  • 并行GC暂停应用程序,只有在没有其他选择时才启动

  • CMS并发运行而不暂停应用程序,有一些开销

来源:http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html

最新更新