目录
1、查看日志
2、查看GC情况
3、分析堆内存对象占用情况
4、分析堆内存快照文件
内存飙高如果发生在java进程上,一般情况是因为创建了大量对象导致,持续飙高说明垃圾回收跟不上对象创建的速度,或者内存泄漏导致对象无法被回收!
排查中涉及到如下命令:
jstat -gc pid 1000 查看gc情况,时间等信息,每隔一秒打印一次
jmap -histo pid | head -20 查看堆内存占用空间最大的钱20个对象类型
jmap -dump:live,format=b,file=/home/chenjian/myheapdump.hprof pid 导出堆内存快照
如果每次gc次数频繁,而且每次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直占用很高;如果每次垃圾回收的内存非常小,那么很可能是因为内存泄漏导致内存一直无法被回收 !
建一个简单的springboot程序,打成jar包,运行:
java -Xmx100m -jar JVMDemo-0.0.1-SNAPSHOT.jar
package com.cjian.jvmdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.Array;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: cjian
* @Date: 2024/2/29 17:27
* @Des:
*/
@RestController
public class TestController {
static List<User> userList = new ArrayList<>();
@GetMapping("/test")
public String test() {
for (int i = 0; i < 30; i++) {
userList.add(new User());
}
return "success";
}
static class User {
byte[] b = new byte[1024 * 1024];
}
}
访问test接口几次后就出现访问失败:
1、查看日志
日志如下:
通过日志发现程序发生了oom,
2、查看GC情况
使用jstat命令查看gc情况:
[root@localhost chenjian]# jps -l
10323 jdk.jcmd/sun.tools.jps.Jps
10217 JVMDemo-0.0.1-SNAPSHOT.jar
[root@localhost chenjian]# jstat -gc 10217 1000 5
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT
0.0 0.0 0.0 0.0 6144.0 2048.0 96256.0 96109.4 31232.0 30374.1 3968.0 3632.8 14 0.600 3 0.117 4 0.007 0.724
0.0 0.0 0.0 0.0 6144.0 2048.0 96256.0 96109.4 31232.0 30374.1 3968.0 3632.8 14 0.600 3 0.117 4 0.007 0.724
0.0 0.0 0.0 0.0 6144.0 2048.0 96256.0 96109.4 31232.0 30374.1 3968.0 3632.8 14 0.600 3 0.117 4 0.007 0.724
0.0 0.0 0.0 0.0 6144.0 2048.0 96256.0 96109.4 31232.0 30374.1 3968.0 3632.8 14 0.600 3 0.117 4 0.007 0.724
0.0 0.0 0.0 0.0 6144.0 2048.0 96256.0 96109.4 31232.0 30374.1 3968.0 3632.8 14 0.600 3 0.117 4 0.007 0.724
输出说明:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
单位:KB
发现,OC:96256.0 OU:96109.4,老年代几乎已经用完了,而且 gc正常,只是没有回收对象;
3、分析堆内存对象占用情况
使用jmap查看堆中占用内存最大的20个对象:
[root@localhost chenjian]# jmap -histo 10217 | head -20
num #instances #bytes class name (module)
-------------------------------------------------------
1: 55050 47757536 [B (java.base@19.0.2)
2: 51933 1246392 java.lang.String (java.base@19.0.2)
3: 7474 889368 java.lang.Class (java.base@19.0.2)
4: 23329 746528 java.util.concurrent.ConcurrentHashMap$Node (java.base@19.0.2)
5: 10386 559528 [Ljava.lang.Object; (java.base@19.0.2)
6: 4249 336384 [I (java.base@19.0.2)
7: 7463 298520 java.util.LinkedHashMap$Entry (java.base@19.0.2)
8: 8796 281472 java.util.HashMap$Node (java.base@19.0.2)
9: 211 278784 Ljava.internal.vm.FillerArray; (java.base@19.0.2)
10: 2940 277704 [Ljava.util.HashMap$Node; (java.base@19.0.2)
11: 274 257888 [Ljava.util.concurrent.ConcurrentHashMap$Node; (java.base@19.0.2)
12: 1990 175120 java.lang.reflect.Method (java.base@19.0.2)
13: 9870 157920 java.lang.Object (java.base@19.0.2)
14: 3146 151008 java.lang.invoke.MemberName (java.base@19.0.2)
15: 2604 145824 java.util.LinkedHashMap (java.base@19.0.2)
16: 2625 105000 java.lang.invoke.MethodType (java.base@19.0.2)
17: 1579 101056 java.net.URL (java.base@19.0.2)
18: 312 100024 [C (java.base@19.0.2)
发现有一个Byte类型的对象共占用了47757536 字节,通过一个数据类型难以判断是哪里的代码问题,我们可以使用如下命令来导出堆内存快照:
[root@localhost chenjian]# jmap -dump:live,format=b,file=/home/chenjian/myheapdump.hprof 10217
Dumping heap to /home/chenjian/myheapdump.hprof ...
Heap dump file created [66982293 bytes in 0.415 secs]
[root@localhost chenjian]# ls
JVMDemo-0.0.1-SNAPSHOT.jar myheapdump.hprof
接着打开jvisualvm工具分析dump文件:
4、分析堆内存快照文件
文件->装入,选择 myheapdump.hprof,
由此可以分析得出是哪里的代码产生的对象导致GC无法回收 !