在什么情况下Java性能会随着内存的增加而降低?

我们正在DEV环境中加载测试Java 1.6应用程序。 JVM堆分配为2Gb,-Xms2048m -Xmx2048m。 在负载测试下,应用程序运行平稳,从不使用超过1.25Gb的堆,垃圾收集完全正常。

在我们的UAT环境中,我们使用相同的参数运行负载测试,唯一的区别是JVM,它分配了4Gb,-Xms4096m -Xmx4096m,否则,硬件与DEV完全相同。 但是在负载测试期间,性能非常糟糕,应用程序几乎吞噬了整个堆,并且垃圾收集运行猖獗。

我们一遍又一遍地运行这些测试,消除了可能影响性能的所有可能症状,但结果是一样的。 在什么情况下可以这样呢?

罪魁祸首可能是垃圾收集,正常的“停止世界”式收集导致我们一些性能问题; 服务器软件运行速度很慢,但服务器的负载也很低。 最终我们发现有一个“停止世界” – 垃圾收集器线程阻止在某些情况下运行的整个软件(产生大量垃圾的操作)。

转向并发垃圾收集缓解了启动参数-XX:+UseParallelOldGC -XX:ParallelGCThreads=8 。 我们在测试和生产中使用“仅”2gb堆,但同样值得注意的是GC占用的时间越长堆(即使您的软件实际上从未使用过所有的堆)。

您可能希望从此处阅读有关不同垃圾收集器 – 选项和调优的更多信息: Java SE 6 HotSpot [tm]虚拟机垃圾收集调整 。

此外,这个问题的答案可以提供一些帮助: Java非常大的堆大小 。

在Production和UAT环境中,您的应用程序有所不同。

从症状来看,(IMO)不太可能是硬件,操作系统性能调整或JVM版本的差异。 不言而喻,这不太可能是由于应用程序具有更多内存。

(你的应用程序可能会做一些奇怪的事情并不是不可思议的……比如根据最大堆大小调整一些数据结构并使计算错误。但我认为你已经意识到这种可能性,所以现在让我们忽略它。)

它可能 OS环境的差异有关 ; 例如,OS的不同版本或某些应用程序,网络的差异,区域设置的差异等等。 但最重要的是99%确定在UAT上运行时应用程序中存在内存泄漏,而内存泄漏正在扼杀堆内存并使GC过载。

我的建议是将此视为存储泄漏问题,并使用标准工具/技术来追踪问题的原因。 在此过程中,您很可能会弄清楚为什么这只会发生在您的UAT上。

分析这两台机器上的堆转储并了解在这两种环境中以不同方式消耗堆的情况是值得的。 直方图将有所帮助。