内存泄漏
什么是内存泄漏?
内存泄露 Memory Leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
Memory Leak会最终会导致Out Of Memory!
模拟内存泄漏
写一段 ThreadLocal 模拟内存泄漏的代码。
/**
* <p>
* 模拟ThreadLocal内存泄露导致OOM
* JVM启动参数 -Xms20M -Xmx20M -Xmn10M
* </p>
*/
@GetMapping("/memoryLeak")
public void memoryLeak() {
// 是否调用remove方法
boolean doRemove = false;
// 加锁,让多个线程串行执行,避免多个线程同时占用内存导致的内存溢出问题
final Object lockObj = new Object();
// 开启20个线程
ExecutorService executorService = Executors.newFixedThreadPool(20);
// 为了不重复使用线程,用Map标记一下已经已使用过的线程,
Map<Long, Integer> threadIdMap = new ConcurrentHashMap<>();
// 循环向线程变量中设置数据 1024 * 1024 = 1M
for (int i = 0; i < 20; i++) {
executorService.execute(() -> {
synchronized (lockObj) {
Integer num = threadIdMap.putIfAbsent(Thread.currentThread().getId(), 1);
if (num == null) {
ThreadLocal<Byte[]> threadLocal = new ThreadLocal<>();
threadLocal.set(new Byte[1024 * 1024]);
// 手工回收
System.gc();
try {
// 调用GC后不一定会马上回收
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
打包部署到服务器。并使用
nohup java -jar -Xms20M -Xmx20M -Xmn10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof & 运行。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof 参数
是为了发生OOM的时候会自动导出Dump文件
部署之后请求一下接口: http://localhost:8080/memoryLeak
服务器上出现报错信息
使用 jstat -gcutil 线程ID 1000 查看,FGC比较频繁,多半是有问题。
接下来我们开始排查
排查过程
打开 heapdump.hprof 文件
文件还是有点大的
我们可以使用JDK自带的 Jvisualvm 打开文件
或者执行 jmap -histo:live 进程ID 查看内存中的存活对象
Byte[] 占用了空间的 46.7%,基本可以断定为是 Byte[] 没有被回收导致的内存泄漏
然后查看代码中使用到 Byte[] 地方的代码即可定位。
IDEA中可以使用 Ctrl + Shift +R 实现全局搜索