JVM排查问题

挑个趁手的好工具

mac中安装MAT,不需要先装Eclipse,可以直接安装MAT。

下载

https://www.eclipse.org/mat/downloads.php

启动

下载完成后解压,到目录mat.app/Contents/MacOS 下启动 ./MemoryAnalyzer -data ./workspace。注意,初始分配的堆大小可能不足,需要去 mat.app/Contents/Eclipse/MemoryAnalyzer.ini 修改-Xmx4

实战

代码 & 运行

用一个在极客时间的例子,在ThreadLocal中分配4Mb的Byte:

@RequestMapping(value = "/test0")
public String test0(HttpServletRequest request) {
    ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();
    localVariable.set(new Byte[4096*1024]);// 为线程添加变量
    return "success";
}

git: https://github.com/yizhanggou/demo

命令:java -jar -Xms256m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<your_folder>/heapdump.hprof -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<your_folder>/heapTest.log demo-0.0.1-SNAPSHOT.jar

实战

测试

请求10000次:
ab -n 10000 http://localhost:8080/test0

因为堆开的太小所以很快就爆OOM了:

--2020-08-22--12.35.36

linux命令初筛

接下来看一下内存占用多少:
--2020-08-22--12.37.16

宿主机是大概3G,即占用了四百多M
--2020-08-22--12.38.04

再看线程占用情况:
--2020-08-22--12.39.33

看nio的线程的jstack:

  1. 获取十六进制
    --2020-08-22--12.44.09

  2. 查看线程状态,可以看到一直处于WAITING
    --2020-08-22--12.44.39

再看jamp占用情况:可以看到老年代几乎占满了。
--2020-08-22--12.46.13

初步判断发生了内存泄漏,那么我们来看一下内存分配情况:占第一排的是Byte
--2020-08-22--12.48.02
说明代码中可能存在Bytes内存泄漏,我们把启动时指定的dump文件下载下来看。(PS:压缩一下会节省很多空间)
--2020-08-22--12.50.31

MAT中查看哪里泄漏

MAT中打开
--2020-08-22--12.52.30

可以看到已经提示有内存泄漏异常:
--2020-08-22--12.53.29

点击进去Class Histogram
--2020-08-22--12.54.17

查看具体哪个对象引用:
--2020-08-22--12.55.41

可以看到是ThreadLocal引用了:
--2020-08-22--12.57.54

纠正

添加remove

添加remove后,就不会爆内存泄漏了:

@RequestMapping(value = "/test0")
public String test0(HttpServletRequest request) {
    ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();
    localVariable.set(new Byte[4096*1024]);// 为线程添加变量
    localVariable.remove();
    return "success";
}

去掉ThreadLocal

改为本地变量也不会爆内存泄漏:

@RequestMapping(value = "/test0")
public String test0(HttpServletRequest request) {
    Byte[] bytes = new Byte[4096*1024];
    return "success";
}

下一篇会讲为什么ThreadLocal会爆内存泄漏。

comments powered by Disqus