JVM的关键系统参数介绍和详细配置( 二 )


1.CMS基本写法
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
因为我们的监控系统会通过JMX监控内存达到90%的状况,所以设置让它75%就开始跑了,早点开始也能减少Full GC等意外情况(概念重申,这种主动的CMS GC,和JVM的老生代、永久代、堆外内存完全不能分配内存了而强制Full GC是不同的概念) 。为了让这个设置生效,还要设置
UseCMSInitiatingOccupancyOnly,否则75%只被用来做开始的参考值,后来还是JVM自己算 。
2. -XX:MaxTenuringThreshold=2
这是改动效果最明显的一个参数了 。对象在Survivor区最多熬过多少次Young GC后晋升到年老代,JDK8里CMS 默认是6,其他如G1是15 。
Young GC是最大的应用停顿来源,而YGC后存活对象的多少又直接影响停顿的时间,所以如果清楚Young GC的执行频率和应用里大部分临时对象的最长生命周期,可以把它设的更短一点,让其实不是临时对象的新生代对象赶紧晋升到年老代,别呆着 。
用-XX:+PrintTenuringDistribution观察下,如果后面几代的大小总是差不多,证明过了某个年龄后的对象总能晋升到老生代,就可以把晋升阈值设小,比如JMeter里2就足够了 。
3. -XX:+
ExplicitGCInvokesConcurrent 但不要加-XX:+DisableExplicitGC
让full gc时使用CMS算法,不是全程停顿,必选 。
但像R大说的,System GC是保护机制(如堆外内存满时清理它的堆内引用对象),禁了system.gc() 未必是好事,只要没用什么特别烂的类库,真有人调了总有调的原因,所以不应该加这个烂大街的参数 。
4. -XX:+ParallelRefProcEnabled 和 -XX:+
CMSParallelInitialMarkEnabled
并行的处理Reference对象,如WeakReference,默认为false,除非在GC log里出现Reference处理时间较长的日志,否则效果不会很明显,但我们总是要JVM尽量的并行,所以设了也就设了 。同理还有-XX:+
CMSParallelInitialMarkEnabled,JDK8已默认开启,但小版本比较低的JDK7甚至不支持 。
5. ParGCCardsPerStrideChunk
Linkined的黑科技,2016版不建议打开,后来发现有些场景的确能减少YGC时间,详见《难道他们说的都是真的》,简单说就是影响YGC时扫描老生代的时间,默认值256太小了,但32K也未必对,需要自己试验 。
-XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=1024
2.2 可选的GC参数1. 并发收集线程数

 
ParallelGCThreads=8+( Processor - 8 ) ( 5/8 );ConcGCThreads = (ParallelGCThreads + 3)/4
比如双CPU,六核,超线程就是24个处理器,小于8个处理器时ParallelGCThreads按处理器数量,大于时按上述公式YGC线程数=18,CMS GC线程数=5 。
CMS GC线程数的公式太怪,也有人提议简单改为YGC线程数的1/2 。
一些不在乎停顿时间的后台辅助程序,比如日志收集的logstash,建议把它减少到2,避免在GC时突然占用太多CPU核,影响主应用 。
而另一些并不独占服务器的应用,比如旁边跑着一堆sidecar的,也建议减少YGC线程数 。
一个真实的案例,24核的服务器,默认18条YGC线程,因为旁边有个繁忙的Service Mesh Proxy在跑着,这18条线程并不能100%的抢到CPU,出现了不合理的慢GC,把线程数降低到12条反而更快了 。所以那些贪心的把YGC线程数=CPU 核数的,通常弄巧成拙 。
2. -XX:-CMSClassUnloadingEnabled
在CMS中清理永久代中的过期的Class而不等到Full GC,JDK7默认关闭而JDK8打开 。看自己情况,比如有没有运行动态语言脚本如Groovy产生大量的临时类 。它有时会大大增加CMS GC的暂停时间 。所以如果新类加载并不频繁,这个参数还是显式关闭的好 。
3. -XX:+CMSScavengeBeforeRemark
默认为关闭,在CMS remark前,先执行一次minor GC将新生代清掉,这样从老生代的对象引用到的新生代对象的个数就少了,停止全世界的CMS remark阶段就短一些 。但如果打开了,会让一次YGC紧接着一次CMS GC,使得停顿的总时间加长了 。
又一个真实案例,CMS GC的时间和新生代的使用量成比例,新生代较小时很快完成,新生代快满时CMS GC的停顿时间超过2秒,这时候就还是打开了划算 。
 
2.3 不建议的GC参数1. -XX:+UseParNewGC
用了CMS,新生代收集默认就是它,不用自己设 。
2.
-XX:CMSFullGCsBeforeCompaction默认为0,即每次Full GC都对老生代进行碎片整理压缩 。Full GC 不同于 老生代75%时触发的CMS GC,只在老生代达到100%,堆外内存满,老生代碎片过大无法分配空间给新晋升的大对象这些特殊情况里发生,所以设为每次都进行碎片整理是合适的,详见此贴里R大的解释 。


推荐阅读