监控工具:arthas
开启dump日志
-XX:+HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个dump.core文件,记录当时的堆内存快照。
-XX:HeapDumpPath=./java_pid.hprof 用来设置堆内存快照的存储文件路径,默认是java进程启动位置。
进行对dump日志进行分析,判断是哪里出现了OOM。
Java heap space错误产生的常见几类
- 请求创建一个超大对象,通常是一个大数组。
- 超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值。
- 过度使用总结器(Finalizer),该对象没有立即被GC
- 内存泄露(Memory Leak),大量对象引用没有释放,JVM无法对其自动回收,常见于使用了File等资源没有回收。
解决方案
针对大部分情况,通常只需要通过 -Xmx参数调高JVM堆内存空间即可。如果仍然没有解决参考进一步处理:
- 如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全结果,而没有做结果数限制
- 如果是业务峰值压力,可以考虑添加机器资源,或者做限制降级。
- 如果是内存泄露,需要找到持有的对象,修改代码设计,比如关闭没有释放的链接。
jstack找出占用cpu最高的线程堆栈信息
package com.mahan.jvm;
/**
* 运行此代码,cpu会飙高
*/
public class Math {
public static final int initData = 666;
public static User user = new User();
public int compute() { //一个方法对应一块栈帧内存区域
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
while (true){
math.compute();
}
}
}
- 出应用进程的PID
ps -ef|grep java
[root@zabbix-agent bin]# jps -l
1217 sun.tools.jps.Jps
1094 org.apache.catalina.startup.Bootstrap
[root@zabbix-agent bin]# ps -ef|grep java
root 1094 1 0 18:35 pts/0 00:00:17 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-0.b14.el7_4.x86_64/bin/java -Djava.util.logging.config.file=/home/apache-tomcat-8.5.31/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /home/apache-tomcat-8.5.31/bin/bootstrap.jar:/home/apache-tomcat-8.5.31/bin/tomcat-juli.jar -Dcatalina.base=/home/apache-tomcat-8.5.31 -Dcatalina.home=/home/apache-tomcat-8.5.31 -Djava.io.tmpdir=/home/apache-tomcat-8.5.31/temp org.apache.catalina.startup.Bootstrap start
或top命令查询CPU使用率PID,然后执行 top -p , 按H,获取每个线程的内存情况
- 找到应用进程的PID为1094,接着找出该进程最消耗CPU的线程,可以使用ps -Lfp 1094 或者top -Hp 1094来查看该进程中线程的cpu消耗情况
[root@zabbix-agent bin]# top -Hp 1094
top - 19:40:51 up 1:52, 1 user, load average: 0.00, 0.01, 0.05
Threads: 44 total, 0 running, 44 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 998628 total, 84224 free, 690548 used, 223856 buff/cache
KiB Swap: 2097148 total, 2068536 free, 28612 used. 146256 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1094 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.00 java
1095 root 20 0 2778656 84996 5324 S 0.0 8.5 0:01.01 java
1096 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.54 java
1097 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.00 java
1098 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.00 java
1099 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.00 java
1100 root 20 0 2778656 84996 5324 S 0.0 8.5 0:03.06 java
1101 root 20 0 2778656 84996 5324 S 0.0 8.5 0:01.46 java
1102 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.00 java
1103 root 20 0 2778656 84996 5324 S 0.0 8.5 0:06.76 java
1104 root 20 0 2778656 84996 5324 S 0.0 8.5 0:00.59 java
- 查看到线程1103的消耗CPU时间是最大的,用命令查看线程的十六进制值
[root@zabbix-agent bin]# printf "%x\n" 1103
44f
- 查询到的值44f 用于下面jstack 定位信息。执行 jstack 1094|grep -A 10 44f,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调 用方法
[root@zabbix-agent bin]# jstack 1094|grep 44f
"VM Periodic Task Thread" os_prio=0 tid=0x00007f602c01bc80 nid=0x44f waiting on condition
这样就可以看到VM Periodic Task Thread 是最耗时的类,然后就可以去代码审查代码了。
docker中没有jvm相关命令?
从宿主机,将jps、jstack、jmap等拷入docker容器中jre的bin目录下