CMS垃圾收集器总结

一 公司CMS参数

先说一下公司CMS参数,年轻代3.5G, 其中survivor 50M。老年代1.5G,其实用不到500M,原空间250M。

二 常见参数配置

  1 开启 CMS

  首先,要说的是,CMS只是老年代的垃圾收集器。其年轻代使用的是ParNew垃圾收集器。

  其次,JDK8默认的垃圾收集器并不是CMS,需要手动指定。-XX:+UseConcMarkSweepGC -XX:+UseParNewGC 

  2 设置堆大小

  使用CMS建议总堆空间不超过8G,最好是6G以内。因为如果发生FGC,太大的堆空间会让FGC的STW时间变的特别的长。

  -Xmx4G -Xms4G -Xmn1512M

  3 线程栈

  JDK8默认的线程栈大小是1M,绝大多数微服务项目可以调整为512K -XSS512K

  4 -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly

   表示只有在老年代达到了70%才进行回收

  5 -XX:MetaspaceSize=314572800  其实是300M

     因为之前发生过元空间引发的FGC,所以我们项目把这个参数调大了。因为本项目的特点是,会请求大量的接口,引入大量的类。

  6 dump路径

   必须配置,因为如果发生了OOM,不dump的话,没法定位问题。

     -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/system/error.dump

  7 GC 日志

   必须配置

      -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 

   -Xloggc:/data/log/gclog/gc.log

  8 压缩

  CMS是一种并发标记清除算法,而不是标记整理算法。所以,它不会进行碎片内存的移动和整理。

  -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

  0表示每次发生fullgc 都进行压缩整理

  9 卸载类

  -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

  如果项目中使用了Netty,或者代码里用了堆外内存,只有FGC才能回收这样的堆外内存。也就是说想手动清理堆外内存就得执行System.gc()。而System.gc()触发的是serial old垃圾收集器,时间会很长,一般在5s左右。而FGC的STW的时间会很长,(CMS的完整FGC是退化为线性GC)。而CMS GC暂停服务的时间较短。因此,加了这个参数可以有效地回收堆外内存并且减少暂停时间。

  这两个参数也是用来改变System.gc()的默认行为用的;不同的 是这两个参数只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()还是会触发GC的,只不过不是触发一个 完全stop-the-world的full GC,而是一次并发GC周期。也还是会触发对外内存的回收。

  

  • CMS采用了Mark-Sweep算法,最后会产生许多内存碎片,当到一定数量时,CMS无法清理这些碎片了,CMS会让Serial Old垃圾处理器来清理这些垃圾碎片,而Serial Old垃圾处理器是单线程操作进行清理垃圾的,效率很低。所以使用CMS就会出现一种情况,硬件升级了,却越来越卡顿,其原因就是因为进行Serial Old GC时,效率过低。
  • 解决方案:使用Mark-Sweep-Compact算法,减少垃圾碎片
  • 调优参数(配套使用):-XX:+UseCMSCompactAtFullCollection 开启CMS的压缩
    -XX:CMSFullGCsBeforeCompaction 默认为0,指经过多少次CMS FullGC才进行压缩
  • 当JVM认为内存不够,再使用CMS进行并发清理内存可能会发生OOM的问题,而不得不进行Serial Old GC,Serial Old是单线程垃圾回收,效率低
  • 解决方案:降低触发CMS GC的阈值,让浮动垃圾不那么容易占满老年代
  • 调优参数:-XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让老年代占用率达到该值就进行CMS GC

  

附上公司启动脚本

  

java -jar -javaagent:/data/gravity/gravity-agent.jar=appName=trade-om-app,baseUrl=http://gravity-api.amh-group.com,appType=jar 
-Xms8192m -Xmx8192m -Xmn5460m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=600m -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/system/error.dump -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
-XX:CMSInitiatingOccupancyFraction=70 -XX:+PrintGCDateStamps -XX:+UseCMSInitiatingOccupancyOnly -XX:+UnlockDiagnosticVMOptions
-XX:+UnsyncloadClass -XX:+UseParNewGC -XX:+PrintGCDetails -Xloggc:/data/applogs/system/gc_202007061528.log

  可以看到 配置eden和survivor比例没有配置,应该是使用默认的。

  

  IBM论文里说据他们统计95%的对象朝生夕死一样存活时间极短,为了保险默认实际使用了90%:设置eden 和survivor(两个)为4:1,每次GC都将Eden和其中一个survivor(from)中的存活对象复制到剩余的survivor(to)中

原文地址:https://www.cnblogs.com/juniorMa/p/15119143.html