6

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

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

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

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

这是在 Linux (RHEL)、Oracle JVM HotSpot 7. 64 位上。4个核心。这是应用程序的运行方式:

java -jar -Xmx4g prog.jar

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

4

3 回答 3

3

听起来您遇到了两个问题之一:

  1. 如果您有半长寿命的对象,它们将从年轻一代移到较旧的一代(例如在 HotSpot 上移至Mark-Compact 一代。)也就是说,它们将停止被引用计数以进行收集并开始保留较慢GC。
  2. 您的年轻代堆空间太小,无法使用强制将对象从年轻代移动到老年代的使用模式。

我会考虑将您的年轻一代调整为更大。请参阅Generational Garbage Collection并查看各种类型的世代。

于 2013-05-13T01:23:51.730 回答
1

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

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

  • 当你有很多垃圾时收集垃圾,并且
  • 垃圾收集时停止其他一切。

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

当然,这意味着您会出现明显的停顿。

如果你想要低延迟,你需要使用不同的收集器。但是,这会导致更大比例的实际 CPU 时间花在垃圾收集上……以及其他与 GC 相关的开销。


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

于 2013-05-13T03:48:26.320 回答
0

这只是因为除非堆满,否则默认 GC 不会启动。

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

如果要定期清理内存,可以切换到 CMS。只需使用标志-XX:+UseConcMarkSweepGC。但是,这将意味着您的应用程序有一些开销。这是在不暂停应用程序的情况下定期运行 GC 的成本。

所以,总结一下:

  • Parallel GC 暂停应用程序,只有在别无选择时才启动

  • CMS 在不暂停您的应用程序的情况下同时运行,有一些开销

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

于 2013-05-13T11:41:25.530 回答