近期发现一个 java 进程内存占用久高不下,重启后恢复,一段时间后又复现。
java
-Xms1024m
-Xmx1024m
-XX:NewSize=256m
-XX:MaxNewSize=256m
-XX:PermSize=64m
// 设置持久代最大值
-XX:MaxPermSize=128m
// 使用 CMS 内存收集
-XX:+UseConcMarkSweepGC
// 降低标记停顿
-XX:+CMSParallelRemarkEnabled
// 在 FULL GC 的时候, 对年老代的压缩, CMS 是不会移动内存的,
// 因此, 这个非常容易产生碎片, 导致内存不够用, 所以, 内存的压缩这个时候就会被启用。
// 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片
-XX:+UseCMSCompactAtFullCollection
// 内存页的大小不可设置过大, 会影响 Perm 的大小
-XX:LargePageSizeInBytes=128m
// 原始类型的快速优化
-XX:+UseFastAccessorMethods
// 使用手动定义初始化定义开始 CMS 收集,禁止 hostspot 自行触发 CMS GC
-XX:+UseCMSInitiatingOccupancyOnly
// 使用 cms 作为垃圾回收使用 70%后开始 CMS 收集
-XX:CMSInitiatingOccupancyFraction=70
// 调试使用,gc 日志输出的相关参数
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-Xloggc:gc.log
-server -jar XXXXX-jar-with-dependencies.jar production
前期 Minor GC 较为频繁,老年代和永久带内存占用不断增加,直到触发 CMS,且老年代与永久代的内存占用没有明显的减少。
初步诊断:
有对象没有正常被回收。
/**
/**
java 使用可达性分析的方式,从 GC root 开始,根据引用关系遍历,所经过的路径形成一棵引用树,没有挂在这棵树上的对象,为不可达对象,可以被回收。
GC root 包括:
针对老年代
标记 - 清除算法 (不进行压缩操作,产生内存碎片)
并发
多线程
收集过程中不需要暂停用户线程
以获取最短回收停顿时间为目标
ParNew + CMS + Serial Old(Concurrent Mode Failure 后备预案)
与用户交互较多的场景。例如:互联网或者 B/S 系统的服务端
-XX:+UseConcMarkSweepGC:使用 CMS 收集器
-XX:+ UseCMSCompactAtFullCollection:Full GC 后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction:设置进行几次 Full GC 后,进行一次碎片整理
-XX:ParallelCMSThreads:设定 CMS 的线程数量(一般情况约等于可用 CPU 数量)
对 CPU 资源非常敏感
浮动垃圾
"Concurrent Mode Failure"失败
内存碎片