目录
- 一、可能产生内存泄露的地方
- 二、复现堆内存泄漏
- 三、如何排查堆内存问题?
- 3.1 获取对内存快照 dump
- 3.2 使用 Visual VM 去分析 dump 文件
- 3.3 定位内存溢出问题
一、可能产生内存泄露的地方
在进行排查 Java 的内存泄漏问题之前,首先我们要知道哪里可能会产生内存泄漏,我们来看看下面这张图:
在运行时数据区中,有三个地方可能会导致内存溢出或者内存泄漏:
- JVM Stacks 虚拟机栈: 报错是 StackOverFlowError,一般指递归造成的问题。
- Method Area/MetaSpace 方法区/元空间: OutOfMemoryError: Metaspace,一般指动态加载的类太多了。
- Heap 堆空间: 报错最常见的,报错是 OutOfMemoryError: java heap space,面试官一般主要关心的也是堆空间内存泄漏的问题,比如一些比较大的对象,一直存活,一直没有被垃圾回收器回收,这个就有可能会导致内存泄露的问题。
二、复现堆内存泄漏
接下来我们演示一下 Heap 堆空间 内存溢出的情况,代码如下:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true) {
list.add("北京");
}
}
这段代码比较简单,就是创建了一个 List,然后不断地向 List 中添加数据。在启动前,为了能够快速的看到我们的测试结果,我们需要添加一个 VM 参数 -Xmx10m
,限制最大堆内存为10M:
这时候再去执行代码,报错如下:
- java.lang.OutOfMemoryError: Java heap space
到目前为止,我们已经成功复现了堆内存泄漏的问题。
三、如何排查堆内存问题?
我们目前是在 IDEA 中进行了堆内存溢出的演示,从控制台可以清楚地看到报错的信息,也提示了具体哪一行代码可能导致内存溢出,我们只要找到这一行代码进行修复就可以了。但是我们的项目一般都是部署在服务器上的,这样的话该怎么排查呢?
举个例子,如下图所示:
比如说我们在服务器中部署了这些服务,这里面的每个微服务都有可能导致内存溢出,严重的话项目可能根本就没有启动起来,或者项目运行一段时间之后宕机了。这些都是有可能的,那这些问题应该怎么排查呢?
针对生产内存溢出问题的排查步骤:
- 获取堆内存快照 dump;
- 使用 Visual VM 去分析 dump 文件。
- 通过查看堆信息的情况,定位内存溢出问题。
下面我们就按照这个步骤去进行排查。
3.1 获取对内存快照 dump
- 如果项目还在运行,我们可以通过
jmap
命令打印它的内存快照 dump。命令如下:
jmap -dump:format=b,file=heap.hprof <pid>
补充: Dump 文件是 Java 进程的内存镜像。可以把程序的执行状态通过调试器保存到 dump文件中。
- 如果项目已经闪退了,我们可以通过在启动命令中添加 vm 参数的方式来生成 dump 文件。命令如下:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/app/dumps
举个例子,我们还是使用上面那段代码:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true) {
list.add("北京");
}
}
只是这次启动的时候,我们要在启动参数中加入我们上面提到的参数:
设置好 vm 参数之后,再次执行代码,结果如下:
可以看到,堆内存快照dump文件已经被存储下来了:
3.2 使用 Visual VM 去分析 dump 文件
接下来,我们来到 JAVA_HOME/bin/ 目录下,找到 Visual VM:
双击打开之后,点击 文件
-> 装入
。
首先切换文件类型为:堆 Dump,选择我们刚才导出的 dump 文件,打开。
打开之后,就可以看到导出的堆内存情况了:
3.3 定位内存溢出问题
从上图中可以看到,Visual VM 已经明确告诉我们程序发生了 OOM 异常,发生在 main 线程中,我们点击 main 就可以看到 main 线程的信息:
从上图中可以看出,这里已经明确提示了具体是哪一行代码出现了错误。我们只要在代码中找到这一行,检查一下这一行代码的上下文,就可以找到并解决掉问题。
以上就是 Java 内存泄漏的排查思路了。
整理完毕,完结撒花~🌻