内存泄漏
什么是内存泄漏?
内存泄露 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 实现全局搜索