如何减lessjava并发模式失败和过度gc

在Java中,并发模式失败意味着并发收集器未能释放足够的内存空间,不得不放弃永久创造,而不得不放弃让全球停止的 gc进入。最终结果可能非常昂贵。

我理解这个概念,但从来没有很好的全面理解
A)什么可能导致并发模式失败
B)解决scheme是什么?

这种不清楚的地方导致我在编写/debugging代码时没有多less提示,经常需要从Foo到Bar的性能标志而没有特别的原因,只需要尝试一下。

我想在这里向开发人员学习你的经验如何? 如果遇到这样的性能问题,原因是什么以及如何解决?

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

我所学到的CMS的第一件事就是比其他collections家需要更多的记忆,大约25%到50%是更好的起点。 这可以帮助你避免碎片化,因为CMS不会像世界collections家所做的那样压缩。 其次,做一些帮助垃圾收集者的事情; Integer.valueOf而不是新的Integer,摆脱匿名类,确保内部类不访问不可访问的东西(外部类私人)这样的东西。 垃圾越less越好。 FindBugs并不忽略警告将有很大的帮助。

至于调整,我发现你需要尝试几件事情:

-XX:+ UseConcMarkSweepGC

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

修复堆的大小:-Xmx2048m -Xms2048m这样可以防止GC不得不做一些事情来增长和缩小堆。

-XX:+ UseParNewGC

年轻一代使用并行而不是串行收集。 这将加快你的小collections,尤其是如果你有一个非常大的年轻人configuration。 一个年轻的年轻一代总的来说是好的,但不要超过老一代的一半。

-XX:ParallelCMSThreads = X

设置CMS在执行可以并行完成的事情时将使用的线程数。

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

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

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

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

-XX:CMSIncrementalDutyCycle = X通过执行GC this%开始

-XX:CMSIncrementalSafetyFactor = X

我已经发现,如果你把它设置成基本上总是在收集,你可以得到一般较低的暂停时间。 由于大部分工作是并行完成的,所以基本上可以预料到暂停。

-XX:CMSFullGCsBeforeCompaction = 1

这个是非常重要的。 它告诉CMS收集器始终完成收集,然后再开始新收集。 如果没有这个,你可能会遇到这样的情况,它会抛出一堆工作,然后重新开始。

-XX:+ CMSClassUnloadingEnabled

默认情况下,CMS会让你的PermGen增长,直到它从现在开始几个星期杀死你的应用。 这停止了​​。 你的PermGen只会在你使用Reflection的时候增长,或者在滥用String.intern,或者在类加载器或者其他一些事情上做一些坏事。

幸存者比例和占有时间也可以根据您是否有长期或短期存在的物体以及在可以生存的幸存者空间之间复制多less对象来进行。 如果你知道所有的东西都会坚持下去的话,那么你可以configuration零大小的幸存者空间,任何能够存活下来的东西都会立即生效。

引自“了解并发标记扫描垃圾收集器日志”

并发模式故障可以通过增加终生代码大小或通过将CMSInitiatingOccupancyFraction设置为较低值来以较小的堆占用启动CMS集合来避免

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

如果您需要快速重新启动和恢复,并喜欢“快速死亡”的方法,我会build议不要使用CMS。 我会坚持'-XX:+ UseParallelGC'。

从“垃圾收集人体工程学”

并行垃圾回收器(UseParallelGC)会在收集less量堆时花费过多的时间。 为了避免这个exception,你可以增加堆的大小。 您还可以设置参数-XX:GCTimeLimit=time-limit-XX:GCHeapFreeLimit=space-limit

有时OOM很快就被杀死了,有时候会长时间(最后一次超过10个小时)。

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

一个CMS故障不会(据我所知)导致一个OOM。 相反,由于JVM需要过快地执行太多的收集,而CMS无法跟上,CMS发生故障。 在很短的时间内发生大量收集周期的一种情况是,当堆堆满时。

真正长的GC时间听起来很奇怪,但是如果你的机器可怕的话,理论上是可能的。 但是,如果你的堆很快满了,那么长时间的重复GC是相当可信的。

您可以将GCconfiguration为在最大大小时以1)放弃,并且2)完全GC完成后仍然接近满。 如果您还没有这样做,请尝试这样做。 它不能解决你的问题,但是至less你的JVM能够快速得到OOM,从而使服务更快速的重启和恢复。

编辑 – 执行此操作的选项是-XX:GCHeapFreeLimit=nnn其中nnn是0到100之间的数字,给出GC之后必须释放的堆的最小百分比。 缺省值为2.该选项在适当标题为“Java 6 JVM的-XX选项的最完整列表”页面中列出 。 (这里列出的许多-XX选项没有出现在Sun的文档中,不幸的是页面没有提供关于这些选项的详细信息。)

你可能应该开始看看你的应用程序/ Web应用程序是否有内存泄漏。 如果有的话,除非发现并修复这些泄漏,否则你的问题不会消失。 从长远来看,摆弄热点GC选项不会修复内存泄漏。

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