一个简单用例的JVM GC设置



我很难为JVM获得正确的设置。

以下是用例:Tomcat正在提供请求(300req/s)。但是它们非常快(键值查找),所以我没有任何性能问题。在我必须每3小时刷新一次它提供的数据之前,一切都会很好。你可以想象我有一个很大的HashMap,我只是在做查找。在数据重载过程中,创建一个临时的HashMap,然后交换它。我需要加载相当多的数据(每次大约800MB的内存)。

我遇到的问题是,在这些加载过程中,Tomcat不时停止响应。最初的问题是升级失败和FullGC,但我通过调整设置来解决这些问题。

正如你可能注意到的,当CMS收集器启动时,我已经降低了价值。我再也没有遇到任何促销失败或类似的事情了。年轻一代的规模相对较小,可以快速收集少量藏品。我增加了SurvivorRatio,因为所有的请求对象都会英年早逝,而没有的对象应该自动升级到老一代。(正在加载的数据)。

但在数据加载过程中,我仍然在Tomcat中看到503个错误。在gc.log中,在此过程中,我的小集合开始变慢。它们现在是以秒为单位,而不是以毫秒为单位。我试着放慢加载过程,让GC喘口气,但我似乎不起作用。。。当我达到老一代的能力时,这个问题尤其成问题。CMS启动,释放内存,然后分配非常缓慢。我在gc.log中没有看到任何错误。我能做些什么不同的事情?我知道碎片化可能是个问题,但我并没有升职失败。该机器是一台8核服务器。减少GCThread的数量有意义吗?为数据加载线程设置较低的线程优先级有意义吗?

有没有办法在后台定期启动CMS收集器?正在交换的数据实际上可以立即被垃圾收集。

我愿意接受任何建议!

以下是我的JVM设置。

-Xms14g
-Xmx14g
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+AlwaysPreTouch 
-XX:MaxNewSize=256m
-XX:NewSize=256m 
-XX:MaxPermSize=128m 
-XX:PermSize=128m 
-XX:SurvivorRatio=24 
-XX:+UseCMSInitiatingOccupancyOnly 
-XX:CMSInitiatingOccupancyFraction=88 
-XX:+UseCompressedStrings 
-XX:+DisableExplicitGC 

JDK 1.6.33Tomcat 6

gc.log片段:

第7行数据加载启动

第20行它停止

http://safebin.net/9124

查看所附的日志,看到小GC时间的巨大增加,我相信您的机器正承受着来自JVM以外的其他进程的极其繁重的负载。

我的理由是,当进行次要GC时,所有应用程序线程都会停止。因此,您的应用程序所做的一切都不应该影响较小的GC时间,因为您的新一代在大小上是恒定的。

但是,如果在此期间机器上的其他进程有大量负载,GC线程将争夺执行时间,您可以看到这种行为。

当你的数据加载正在运行时,你能检查其他进程的CPU使用情况吗?

编辑:在日志上多看一点,我想出了另一个可能的解释。

目标幸存者空间似乎已经满了(ParNew每次"慢速"GC下降到10048K)。这意味着对象被直接提升到旧一代,这可能会减缓速度。我会尝试增加新一代的规模,降低幸存者比例。甚至可能尝试在根本不设置新一代大小或存活率的情况下运行,看看JVM是如何优化的(尽管要注意,JVM通常在优化这样的突发事件方面做得很差)。

您的负载持续约90秒,每隔1秒左右就会被一个GC中断,但您有一个14G堆,它的稳态占用率(假设周围的日志行是稳态)仅为5G左右,这意味着您将浪费大量内存。我认为之前的答案看起来是正确的(根据提供的数据),当它说你的幸存者空间太小时。如果它在剩下的时间里除了查找什么都不做,那么一个完全合理的策略就是

  • 衰减阈值=0(或1)
  • eden大小>工作集的2倍,因此可能为1.5-2G(即允许当前实时数据和工作副本完全位于eden中)
  • 终身制

这里的重点是在加载阶段尽量完全避免年轻的集合。然而,0的终身阈值意味着以前的版本可能是终身的,你最终会看到一个可能很长的集合来清理它。另一种选择可能是反其道而行之,拥有足够大的终身教职,以容纳2-3个版本的数据,并对其余数据进行编辑,以尽量减少年轻收集的频率,并帮助尽快收集终身教职。

什么最有效实际上取决于应用程序在剩下的时间里还做什么。

对于一个大堆btw来说,cms触发器似乎很高,如果你只在88%的时候开始收集,那么它有时间在强制执行fullgc之前完成任务吗?我想,如果你大部分时间都在做少量的分配,这可能是非常安全的。

最新更新