内存溢出问题定位
前言:内存溢出是程序运行中很常见的一个问题,导致这个问题发生的可能性也有很多,如内存分配不足或者内存泄漏等,如何定位到问题所在是非常重要的,至此记录一下关于定位内存溢出问题的方法与思路。(在实验过程出现了一个关于maven版本冲突的问题:解决链接)
主要工具:jmap、mat工具
步骤:
- 先写出两个demo代码:一个不断创建对象导致堆内存溢出,一个不断创建类导致元空间内存溢出。
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MemoryController {
private List<User> userList = new ArrayList<User>();
private List<Class<?>> classList = new ArrayList<Class<?>>();
/**
* 设置堆内存大小
* -Xmx32M -Xms32M
* */
@GetMapping("/heap")
public String heap() {
int i=0;
while(true) {//这里的User是一个随便创建的类
userList.add(new User(i++, UUID.randomUUID().toString()));
}
}
/**设置元空间内存大小
* -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M
* */
@GetMapping("/nonheap")
public String nonheap() {
while(true) {//关于此方法的详细代码在我关于maven版本冲突博客中
classList.addAll(Metaspace.createClasses());
}
}
}
- 运行程序,在VM参数上设置堆内存大小为32M
- 访问网址/heap路径运行测试代码,出现
java.lang.OutOfMemoryError: GC overhead limit exceeded
的异常。 - 导出内存映像文件(两种方法)
1、VM参数方法:内存溢出自动导出
//设置自动导出以及导出到当前路径
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
2、jmap
命令:先运行对应的jar进程,jps
找到该进程ID,然后jmap设置导出格式
jps
jmap -dump:format=b,file=heap.hprof <pid>
- 将,hprof文件导入到MAT工具中进行分析
很显然,MAT工具已经分析出了占用内存最大的方法所在的类,再进一步分析观察所在类那个对象出现了问题
- 元空间内存溢出分析与堆分析思路大体相同
总结:
1、先导出内存映射文件.hprof(两种方法:VM参数、jmap)
2、将导出的文件用MAT工具进行分析,定位出问题所在,修复代码。
3、在实际开发中,内存溢出的问题定位很复杂,在工具的帮助下一步步分析定位到问题代码是开发中非常重要的能力。
导致内存溢出的常见情况:
1、大量使用静态字段
2、未关闭资源导致内存泄漏(数据库连接)
3、不当的equal和hashcode会导致内存泄漏从而导致内存溢出
4、ThreadLocal