我学习到的关于CMS的第一件事是,它需要比其他收集器更多的内存,增加25%到50%是一个好的起点。这有助于避免碎片化,因为CMS不像停止世界(stop the world)收集器那样进行任何压实操作。其次,要做一些有助于垃圾收集器的事情:使用Integer.valueOf而不是new Integer,摆脱匿名类,确保内部类不会访问无法访问的内容(外部类中的private等)。垃圾越少越好。在这方面,FindBugs和不忽略警告将对此有很大帮助。
至于调优,我发现你需要尝试几件事情:
-XX:+UseConcMarkSweepGC 告诉JVM在旧生代中使用CMS。
固定堆的大小:-Xmx2048m -Xms2048m 这可以防止GC做像扩大和缩小堆之类的事情。
-XX:+UseParNewGC 在年轻代中使用并行(parallel)而不是串行(serial)的收集。特别是如果你配置了非常大的年轻代,则这将加快你的小型收集。大的年轻代通常很好,但不要超过老年代大小的一半。
-XX:ParallelCMSThreads=X 设置CMS在并行执行操作时使用的线程数。
-XX:+CMSParallelRemarkEnabled 默认情况下remark是串行的,这可以加速您的程序。
-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区域持续增长,直到几周后杀死你的应用程序。这个选项可以停止PermGen的增长。如果您使用了反射(Reflection),或者滥用String.intern,或者使用类加载器做一些不好的事情,那么PermGen可能会不断增长。
根据对象的生命周期长短以及在survivor空间之间进行的对象复制量,也可以调整Survivor比率和tenuring阈值。如果你知道所有的对象都会保留下来,可以配置零大小的survivor空间,任何在young gen收集中幸存下来的对象都将立即进入老年代。