一、问题现场
在项目中有一个单节点的task服务,这服务有由于架构原因无法实现多节点部署,该服务每隔2个月左右时间会出现异常,内存一直飙升,但jvm 并没有进行full gc ,直至服务tomcat 出现假死,服务异常。
二、问题分析
该服务节点以前是在物理机上运行,最近我们将该服务部署到了docker 容器中,并且将jdk从1.6升级到了1.8,然后就出现了上述问题。
- 出现问题我们接入了公司统一的jvm监控平台,并配置了jvm内存使用量监控;
- 通过jvm内存监控发现:堆内存不断飙升的时候,但是没有触发full gc;
- 通过jmap导出,堆栈快照的时候没有发现异常信息;
- 发现服务用的是jdk 1.8 默认的垃圾收集器:Parrallel GC
三、解决方案
- 找运维配置一台8g 内存8核CPU 的docker 容器;
- 将默认 Parrallel GC 更换为 CMS GC ;
- 加jvm堆栈监控日志,用于分析生产环境GC 情况;
四、jvm参数设置
4.1 配置jvm参数
echo "origin OPTS is: $CATALINA_OPTS"
CATALINA_OPTS="$CATALINA_OPTS -Xms2048m -Xmx4096m -XX:NewSize=1g -XX:MaxNewSize=1g"
JAVA_OPTS="$JAVA_OPTS -verbose:gc -XX:+DisableExplicitGC -XX:ParallelGCThreads=4 -XX:+PrintGCDetails
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
-Xloggc:/tomcat8/logs/gc.log -XX:ErrorFile=/tomcat8/logs/hs_err_pid%p.log "
参数解释:
-Xms2048m:设置JVM初始堆内存为2048m 。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmx4096m :设置JVM最大堆内存为4096m。
-XX:NewSize=1g :设置年轻代初始值为1g。
-XX:MaxNewSize=1g :设置年轻代最大值为1g 。
-XX:+DisableExplicitGC:不响应 System.gc() 代码。
-XX:ParallelGCThreads=4:配置并行收集器的线程数,即同时4个线程一起进行垃圾回收。此值一般配置为与CPU数目相等。
-XX:+PrintGCDetails:每次GC时打印详细信息。
-XX:+PrintGCDateStamps:GC发生的时间信息。
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。
-XX:+PrintGCApplicationConcurrentTime :打印每次垃圾回收前,程序未中断的执行时间。
-XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
-XX:CMSInitiatingOccupancyFraction=75:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
-XX:+UseCMSInitiatingOccupancyOnly:使用手动定义初始化定义开始CMS收集。
-Xloggc:把相关日志信息记录到文件以便分析。
-XX:ErrorFile=/tomcat8/logs/hs_err_pid%p.log:保存错误日志或数据到指定文件中。
4.2 tomcat 中配置jvm方法
1)windows 环境 解压版本的Tomcat, 通过startup.bat启动tomcat加载配置的,在tomcat 的bin 下catalina.bat 文件内添加;
2)Linux服务器Tomcat设置JVM,修改TOMCAT_HOME/bin/catalina.sh;