由于我们在生产环境中不能随意打印log日志,也不能随意更新代码重启生产环境的服务,所以当生产环境发生内存溢出时我们应该采取什么措施来找出问题所在尼?
一、模拟内存溢出
@RestController
public class MemoryController {
private List<User> userList = new ArrayList<User>();
/**
* -Xmx32M -Xms32M
* @return
*/
@GetMapping("/heap")
public String heap(){
int i = 0;
while (true){
userList.add(new User(i++, UUID.randomUUID().toString()));
}
}
}
注释:请求
http://localhost:8080/heap
,不断往userList中添加User对象,由于userList是MemoryController的属性不会被回收,故导致内存溢出。
在启动时配置jvm参数:-Xmx32M -Xms32M (使得程序尽快能够内存溢出,不用等待很长时间)
二、生成heap dump文件
我们选择以下任意一种方式来获取一份heap dump文件:
1.自动生成
运行前添加jvm参数:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
当内存溢出时会在当前目录下自动生成heap dump文件
2.手动执行
当发生内存溢出时在控制台手动输入命令:
jmap -dump:format=b,file=heap.hprof 4063
其中4063为java程序运行的pid,运行命令后会在执行目录下生成heap.hprof文件
三、使用mat工具分析
下载mat(
下载地址
),下载mac版本,双击运行。
选择我们上面模拟生成的heap dump文件,加载完成后。
首页:
点击Leak Suspects查看可能产生泄露的原因:
首先怀疑的是MemoryController这个类总共占用了62.26%的内容
点击标题栏第二个按钮,输入包名正则,查看对象在内存中的数量
我们可以看到产生了110476个User对象,可以看出这明显是不正常的现象
下面查看该对象的强引用
可以看出MemoryController对象的userList属性中存在110476个User对象,于是我们就找到了内存泄露的真正原因了。
我们还可以从对象占用内存比例来查找原因,点击标题栏第三个图标,输入包名正则表达式,可以看到按照内存占用的排序结果,这也同样反应了问题所在