原创

Linux环境下java线上问题解决方案

线上问题一般有两类:

CPU 持续飙高

内存泄漏

下面分别针对两种问题一步一解决。

CPU 持续飙高

经验中有两种情况会出现 cpu 飙高,分别是死循环或者频繁 GC

解决方案:

1.通过 top 命令查看 pid

top

2.找到子进程

top -H -p pid

3.查看 16 进制编号

printf %x subPid

4.查看堆栈线程状态

jstack pid >tmp

5.通过查找 tmp 里面的 16 进制 id 可以找到对应的线程是哪个,进而解决

及早发现问题:

加监控,有了监控,当某个 java 服务不正常的时候就会报警,这样就实现了有问题提前感知。

脚本 checkJavaCpu.sh

#!/bin/sh
ShellAbsPath=$(cd "$(dirname "$0")"; pwd)
function checkCpu(){
        ID=$1
        pid=`ps aux|grep "dubbo${ID}"|grep -v "grep"|awk '{print $2}'|head -n 1`
        nowCpu=`top -b -n 1 | grep ${pid} | awk '{print int($9)}'`
        if [ ${nowCpu} -gt 200 ];then
                TimeLike=`date "+%d/%b/%Y:%H:%M"`
                #sendMail.sh
        fi;
}
IDArr=('1' '2');
length=${#IDArr[@]}
for ((i=0; i<${length};i++))
do=${I
        IDDArr[$i]}
        checkCpu ${ID}
done

添加定时任务

* */1 * * * /home/gameboys/checkJavaCpu.sh

大内存线上内存泄漏排查

前提是内存,意思 down 到本地使用 window 工具排查不太可能,因为 dump 出来的文件基本上 2G 以上,一般出现内存泄漏多半是 ConcurrentHashMap 越来越大导致,笔者遇到过不下于 5 次,所以很多情况下不确定能清理的数据使用 LRUMap,处理这类问题先使用jstat -histo查看占用较大的对象,然后对应代码分析,20%的几率是可以解决,如果没法定位,那么使用终极武器:

1.下载 linux 的 mat 工具

下载地址:http://www.eclipse.org/mat/downloads.php

运行 uname -m 看一下 linux 是 x86_64 还是 x86 的帮助你选择下载那个版本。

uname -m

wget http://mirrors.neusoft.edu.cn/eclipse/mat/1.9.0/rcp/MemoryAnalyzer-1.9.0.20190605-linux.gtk.x86_64.zip

2.解压

unzip MemoryAnalyzer-1.9.0.20190605-linux.gtk.x86_64.zip

3.配置 mat

修改 MAT 的内存大小, 注意这个大小要根据你 dump 文件大小来的,如果 dump 文件是 5GB 那么 这里最好配>5GB 否则会报 MAT 内存不足的异常

修改 MemoryAnalyzer.ini 的 -Xmx6024m

4.dump 出 java 的内存信息

jmap -dump:live,format=b,file=tmp.hprof pid

5.使用脚本在 linux 上面分析(注:服务器空闲内存必须大于配置内存,如果内存不够,则先压缩之后 scp 到一台空闲服务器处理)

./ParseHeapDump.sh /tmp.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components

6.下载分析好的文件并解压,通过浏览器打开分析,

三个文件分别为:a_Leak_Suspects.zip a_System_Overview.zip a_Top_Components.zip

保留现场脚本

dump.sh

#!/bin/bash

cd `dirname $0`
BIN_DIR=`pwd`
PID=$1
DATE=`date +%Y%m%d`
DATE_DIR=${BIN_DIR}/dump/$DATE
#创建目录,如果没有的时候
mkdir -p ${BIN_DIR}/dump/\$DATE

jstack $PID > $DATE_DIR/jstack-$PID.dump 2>&1
echo -e ".\c"
jinfo $PID > $DATE_DIR/jinfo-$PID.dump 2>&1
echo -e ".\c"
jstat -gcutil $PID > $DATE_DIR/jstat-gcutil-$PID.dump 2>&1
echo -e ".\c"
jstat -gccapacity $PID > $DATE_DIR/jstat-gccapacity-$PID.dump 2>&1
echo -e ".\c"
jmap $PID > $DATE_DIR/jmap-$PID.dump 2>&1
echo -e ".\c"
jmap -heap $PID > $DATE_DIR/jmap-heap-$PID.dump 2>&1
echo -e ".\c"
jmap -histo $PID > $DATE_DIR/jmap-histo-$PID.dump 2>&1
echo -e ".\c"
if [ -r /usr/sbin/lsof ]; then
/usr/sbin/lsof -p $PID > $DATE_DIR/lsof-$PID.dump
echo -e ".\c"
fi

if [ -r /bin/netstat ]; then
/bin/netstat -an > $DATE_DIR/netstat.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/iostat ]; then
/usr/bin/iostat > $DATE_DIR/iostat.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/mpstat ]; then
/usr/bin/mpstat > $DATE_DIR/mpstat.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/vmstat ]; then
/usr/bin/vmstat > $DATE_DIR/vmstat.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/free ]; then
/usr/bin/free -t > $DATE_DIR/free.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/sar ]; then
/usr/bin/sar > $DATE_DIR/sar.dump 2>&1
echo -e ".\c"
fi
if [ -r /usr/bin/uptime ]; then
/usr/bin/uptime > $DATE_DIR/uptime.dump 2>&1
echo -e ".\c"
fi
echo "OK!"
echo "DUMP: $DATE_DIR"
正文到此结束