如何减少java并发模式失败和过多的gc

时间:2010-05-27 02:32:20

标签: java garbage-collection concurrency

在Java中,并发模式失败意味着并发收集器无法释放足够的内存空间形成终身和永久性,并且必须放弃并让完整的停止世界 gc踢in。最终结果可能非常昂贵。

我理解这个概念但从未对中文有过全面的了解 A)什么可能导致并发模式失败和 B)解决方案是什么?

这种不清楚导致我编写/调试代码而没有太多提示,并且经常不得不在没有特殊原因的情况下从Foo到Bar那些性能标记,只需要尝试。

我想向开发人员学习您的体验?如果您遇到此类性能问题,原因是什么以及您如何解决它?

如果您有编码建议,请不要太笼统。谢谢!

4 个答案:

答案 0 :(得分:22)

我学到的关于CMS的第一件事就是它需要比其他收藏家更多的内存,大约25%到50%是一个很好的起点。这可以帮助您避免碎片,因为CMS不会像世界收藏家那样做任何压缩。第二,做有助于垃圾收集者的事情; Integer.valueOf而不是新的Integer,摆脱匿名类,确保内部类不访问不可访问的东西(在外部类中是私有的)这样的东西。垃圾越少越好。 FindBugs而不是忽略警告将对此有很大的帮助。

就调整而言,我发现你需要尝试几件事:

-XX:+ UseConcMarkSweepGC

告诉JVM在终身使用中使用CMS。

修复堆的大小:-Xmx2048m -Xms2048m这可以防止GC执行诸如增长和收缩堆之类的操作。

-XX:+ UseParNewGC

在年轻一代中使用并行而非串行收集。这将加速你的次要收藏,特别是如果你配置了一个非常大的年轻人。一个年轻的大一代通常都很好,但不要超过旧一代的一半。

-XX:ParallelCMSThreads = X

设置CMS在执行可以并行执行的操作时将使用的线程数。

-XX:+ CMSParallelRemarkEnabled备注默认是串行的,这可以加快你的速度。

-XX:+ CMSIncrementalMode允许应用程序通过在阶段之间插入GC来运行更多

-XX:+ CMSIncrementalPacing允许JVM计算随时间变化收集的频率

-XX:CMSIncrementalDutyCycleMin = X最少花费在GC上的时间

-XX:CMSIncrementalDutyCycle = X从这个%的时间开始做GC

-XX:CMSIncrementalSafetyFactor = X

我发现如果你设置它可以获得通常较低的暂停时间,以便它基本上总是收集。由于大部分工作是并行完成的,因此最终会出现基本上可定期的可预测暂停。

-XX:CMSFullGCsBeforeCompaction = 1

这一点非常重要。它告诉CMS收集器在开始新集合之前始终完成集合。如果没有这个,你可能会遇到这样的情况:它会抛出一堆工作并重新开始。

-XX:+ CMSClassUnloadingEnabled

默认情况下,CMS会让你的PermGen成长,直到它在几周后杀死你的应用程序。这样就可以了。如果您使用Reflection,或者滥用String.intern,或者使用类加载器或其他一些东西做坏事,那么PermGen只会增长。

幸存者比率和持久时间也可以使用,具体取决于你是否拥有长期或短期物体,以及在你可以忍受的幸存者空间之间复制多少物体。如果你知道你的所有物体都会被粘住,你可以配置零大小的幸存者空间,任何幸存下来的年轻人都可以立即使用。

答案 1 :(得分:11)

引自"Understanding Concurrent Mark Sweep Garbage Collector Logs"

  

并发模式失败也可以   通过增加终身职位来避免   生成大小或启动CMS   在较小的堆占用率的集合   通过设置   CMSInitiatingOccupancyFraction到   价值较低

但是,如果您的应用程序中确实存在内存泄漏,那么您只是在购买时间。

如果您需要快速重启和恢复并且更喜欢“快死”方法,我建议您根本不使用CMS。我会坚持使用'-XX:+ UseParallelGC'。

来自"Garbage Collector Ergonomics"

  

并行垃圾收集器   (UseParallelGC)抛出一个   如果一个内存异常   过多的时间   花了少量的收集   堆。为了避免这种例外,你可以   增加堆的大小。您可以   还设置参数   -XX:GCTimeLimit=time-limit-XX:GCHeapFreeLimit=space-limit

答案 2 :(得分:3)

  

有时OOM很快就会被杀死,有时候会遇到很长的gc期(上次超过10个小时)。

听起来像内存泄漏是你问题的根源。

CMS失败(根据我的理解)不会导致OOM。而是发生CMS故障,因为JVM需要过快地执行过多的集合,而CMS无法跟上。在很短的时间内发生大量收集周期的一种情况是你的堆几乎满了。

非常长的GC时间听起来很奇怪......但理论上如果你的机器可怕地颠簸可能。但是,如果您的堆几乎已满,则长时间重复的GC非常合理。

您可以将GC配置为当堆为1)时以最大大小放弃,2)在完整GC完成后仍然接近满。如果您还没有这样做,请尝试这样做。它不会解决您的问题,但至少您的JVM将快速获得OOM,从而加快服务重启和恢复速度。

编辑 - 执行此操作的选项是-XX:GCHeapFreeLimit=nnn,其中nnn是0到100之间的数字,表示GC后必须释放的最小堆百分比。默认值为2.该选项列在恰当标题为"The most complete list of -XX options for Java 6 JVM"的页面中。 (其中列出了许多-XX选项,这些选项未出现在Sun文档中。不幸的是,该页面提供了有关选项实际执行情况的一些详细信息。)

您应该开始查看您的应用程序/ webapp是否有内存泄漏。如果有的话,除非发现并修复了这些泄漏,否则您的问题不会消失。从长远来看,摆弄Hotspot GC选项不会解决内存泄漏问题。

答案 3 :(得分:0)

我发现使用-XX:PretenureSizeThreshold=1m使'大'对象立即进入终身空间大大减少了我的年轻GC和并发模式失败,因为它往往不会尝试转储年轻的+ 1幸存者数据量(完整的CMS周期完成之前xmn=1536m survivorratio=3 maxTenuringThreashould=5)。是的,我的幸存者空间很大,但是大约有一天需要它的应用程序(我们每天为1个应用程序运行12个app服务器)。