Java应用程序变得越来越慢,直到执行完整的GC

时间:2014-07-28 14:34:54

标签: java performance grails garbage-collection jvm

我有一个程序接收UDP数据包,从中解析一些数据,并将其保存到多个线程的数据库中。它使用Hibernate和Spring via Grails(GORM独立)。

它可以在一台服务器上正常工作,它可以快速启动(每个数据包20-30毫秒 - 除了JIT启动时的第一个 - )并在一段时间内稳定在50-60毫秒。

但是,在更新,功能更强大的服务器中,它会快速启动但逐渐变慢(每个数据包达到200毫秒甚至300毫秒,总是使用相同的负载)。然后,当JVM执行完整的GC(或者我从Visual VM手动执行)时,它会再次变快并且循环重新开始。

关于什么可能导致此行为的任何想法?随着Old Gen填满,它似乎变得越来越慢。伊甸园填补速度很快,但GCs暂停似乎很短暂。它在旧服务器上运行正常,所以让我感到困惑。

服务器和设置:

服务器规格是:

  • 旧服务器:Intel Xeon E3-1245 V2 @ 3.40GHz,32 GB RAM无ECC
  • 新服务器:Intel Xeon E5-1620 @ 3.60GHz,带ECC的64 GB RAM
  • 操作系统:Debian 7.6
  • JVM:
    • java版“1.7.0_65”
    • Java(TM)SE运行时环境(版本1.7.0_65-b17)
    • Java HotSpot(TM)64位服务器VM(内置24.65-b04,混合模式)
  • JVM设置:
    • 旧服务器:运行时没有特殊的RAM或GC参数,PrintFlagsFinal给出:-XX:InitialHeapSize = 525445120 -XX:+ ManagementServer -XX:MaxHeapSize = 8407121920 -XX:+ UseCompressedOops -XX:+ UseParallelGC
    • 新服务器:尝试强制执行相同的标记,结果相同。
    • 旧服务器似乎支持UseFastStosb,默认情况下已启用。在新服务器中强制它会产生一条消息,表明它不受支持。

3 个答案:

答案 0 :(得分:2)

您可以尝试使用JVM版本支持的G1吗?

如果应用程序具有以下一个或多个特征,则使用CMS或Parallel Old GC垃圾收集器运行的应用程序将有助于切换到G1。

(1)完整的GC持续时间太长或太频繁。 (2)对象分配率或促销率差异很大。 (3)不期望的长垃圾收集或压实暂停(超过0.5至1秒)

答案 1 :(得分:0)

我不可能说您的应用程序/服务器VM默认值是否有任何问题。

尝试添加-XX:+ PrintGCDetails以了解有关垃圾收集时年轻和老一代的更多信息。根据这些值,您的初始堆大小约为525MB,最大堆大约为8.4GB。 JVM将根据需求调整堆的大小,并且每次调整此堆的大小时,所有年轻代和旧代都会相应调整大小,这将导致完整GC。

此外,您的标志指示UseParallelGC,它将使用多个线程执行年轻代集合,但旧的gen仍然使用单线程连续收集。

NewRatio的默认值是2,这意味着Young gen占用堆的1/3,旧版占用堆的2/3。如果你有太多的短生命对象,请尝试调整年轻的基因大小,并且现在尝试使用7u65可能会尝试使用G1 GC。

但在调整之前我强烈建议你 (1)对GC日志进行适当的分析 - 在慢响应时间内查看是否有任何Full GC (2)尝试Java Mission Control。用它来监控远程服务器进程。它的功能丰富,你会知道更多关于GC的信息。

答案 2 :(得分:0)

您可以使用-XX:+ PrintGCDetails选项查看每个GC的发生频率。

但是,我不认为这是GC问题(或GC papameters)。正如您在帖子中所说的那样,程序运行正常,但当它被移动到新的快速机器时会出现问题。我的猜测是你的程序存在一些瓶颈,这反过来会减慢对已分配对象的释放引用。因此,内存会累积,VM会花费大量时间进行GC和内存分配。

在其他方法中,为包进程分配堆内存的过程,以及使用者将包内存保存到DB后回收此内存。但消费者无法赶上手续速度。

所以我的建议是检查你的程序并做一些测量