我有一个Java应用程序,它在不同的环境中显示不同的GC行为.在一个环境中,堆使用图是一个缓慢的锯齿,每10个小时左右就有一个主要的GC,只有当堆大于90%时才会填满.在另一个环境中,JVM每小时在点上执行主要GC(在这些时间堆通常在10%到30%之间).
我的问题是,导致JVM决定执行主要GC的因素是什么?
显然它会在堆几乎满了时收集,但是还有一些其他原因在起作用,我猜这与我应用程序中的每小时计划任务有关(尽管此时内存使用没有峰值).
我假设GC行为在很大程度上取决于JVM; 我在用:
Java HotSpot(TM)64位服务器VM 1.7.0_21 Oracle Corporation
没有特定的GC选项,因此使用64位服务器的默认设置(PS MarkSweep和PS Scavenge)
其他信息:
这是在Tomcat 6中运行的Web应用程序.
Perm gen在两种环境中都徘徊在10%左右.
具有锯齿行为的环境具有7Gb最大堆,另一个具有14Gb.
拜托,没有猜测.JVM必须具有用于决定何时执行主要GC的规则,并且这些规则必须在源中深处编码.如果有人知道它们是什么,或者它们被记录在哪里,请分享!
我发现了四个可能导致主要GC的条件(给定我的JVM配置):
旧的gen区域已满(即使它可以生长,主要GC仍将首先运行)
perm gen区域已满(即使它可以生长,主要GC仍将首先运行)
有人手动调用System.gc()
:与RMI坏库或东西(见链接1,2和3)
年轻的一代都很满,没有任何东西可以搬进老一代(见1)
正如其他人所评论的那样,案例1和案例2可以通过分配大量的堆和permgen,并设置-Xms
和-Xmx
相同的值(以及perm等价物)来改进,以避免动态堆调整大小.
使用-XX:+DisableExplicitGC
标志可以避免案例3 .
案例4需要更多参与调整,例如-XX:NewRatio=N
(参见Oracle的调优指南).
垃圾收集是一个非常复杂的主题,虽然您可以了解有关此问题的所有详细信息,但我认为您的案例中发生的事情非常简单.
Sun的垃圾收集调整指南,在"明确的垃圾收集"标题下,警告:
应用程序可以与垃圾收集进行交互...通过显式调用完整的垃圾收集...这可以强制在可能不需要时完成主要收集... 显式垃圾收集的最常见用途之一是RMI ... RMI强制定期收集完整收集
该指南说垃圾收集之间的默认时间是一分钟,但sun.rmi属性参考,在下面sun.rmi.dgc.server.gcInterval
说:
默认值为3600000毫秒(一小时).
如果您在一个应用程序中每小时都看到主要集合而不是另一个应用程序,则可能是因为应用程序正在使用RMI,可能仅在内部,并且您尚未添加-XX:+DisableExplicitGC
到启动标志.
禁用显式GC,或通过设置-Dsun.rmi.dgc.server.gcInterval=7200000
和观察GC是否每两个小时发生一次来测试此假设.