一、外在表现
前段时间系统经常出现OOM,服务很不稳定,偶尔会有java进程不存在的情况,临时解决方案只能是重启。
二、辅助工具
1.top
用top查看,发现内存占用(%MEM)挺多,其他指标均正常。
2.dmesg
如果发现自己的java进程突然消失了,那么就要借助dmesg来查看开机之后的系统日志
命令为dmesg | grep -i 'kill'或者搜索oom(out of memory),如果能搜索到相关信息,则说明java进程是被操作系统kill了,操作系统有一种机制,它会在机器的内存耗尽前,挑选几个占用内存较大的进程杀死(实际也是有一定的计算规则),通常被杀死的就是java进程,那么接下来就是看看是什么原因造成内存这么大。dmesg 输出的格式不易查看,可以通过命令进行转换。date -d "1970-01-01 UTC `echo "$(date +%s)-$(cat /proc/uptime|cut -f 1 -d' ')+12288812.926194"|bc ` seconds"
三、排查过程
OOM的原因一般为内存泄露,创建了对象不能释放,也有可能是突然间创建了大对象,有时加载过多的class也是原因。线上遇到OOM需要做两件事情第一个是dump内存,第二个是看下GC日志。
1. jps -mlvV (或者 ps -ef | grep java)
找出当前java进程号1234
linux环境下可能要先执行
export JAVA_HOME=/*/*
export PATH=$JAVA_HOME/bin:$PATH
2. jstat -gcutil 1234 1000 10
从这一步查出,full gc次数频繁,由此可见原因是老年代空间不足
3. dump内存
接下来就是排查问题最重要的一步,dump内存最容易想到的是
jmap -dump:format=b,file=heap.hprof 1234。
注意如果用jmap来dump的话,一来非常慢,二来可能会出异常,在linux JDK1.6某个版本里使用jmap可能会让系统挂掉,可以通过-d64来解决(jmap -J-d64 -dump:format=b,file=dump.bin PID)。而jdk7的某个版本则会抛出异常,这是jdk的bug.
一般dump下来的内存有几个G,而有时候dump下来只有一两百兆,说明jmap有问题,需要多执行几次jmap -dump才能得出正常结果,这个时候可以选用
gcore 把整个内存dump出来,然后再使用jmap把core dump转换成heap dump。
做法就是用gcore 1234命令来生成c版的core文件,再用命令jmap -dump:format=b,file=heap.hprof /bin/java core.1234.
4. 使用eclipse MAT或者visaul VM查看dump文件分析原因
在用eclipse分析dump文件时有可能因为文件过大而报异常,这时要调大eclipse本身占用的堆内存大小,在eclipse.ini文件里面,因为eclipse本身也是一个java程序。
通过自动dump下来的内存文件很快发现有一个对象占用内存非常大,解决后系统恢复正常。
5. 查看gc日志
排查OOM通常要结合tomcat的日志、gc日志来查看。如果没有任何JVM参数设置,gc日志默认打印在stdout.log文件里,里面可能会打其他的日志,而且GC日志也不会输出时间,所以在JVM启动参数里最好加以下命令,规范下GC日志输出到/home/admin/logs/gc.log,并且打印GC时间。
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/admin/logs
-Xloggc:/home/admin/logs/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps