jmap MAT内存溢出实践

 

一、创建Spring Boot工程


jmap MAT内存溢出实践_内存溢出

点击创建工程,然后用Idea或者Eclipse打开

 

二、创建模拟Heap内存溢出的代码

1、创建MemoryController类

jmap MAT内存溢出实践_spring_02

 

2、创建User类



public class User {
private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}


  

3、 设置最大内存和最小内存为32M

-Xmx32M -Xms32M

jmap MAT内存溢出实践_tomcat_03

 

4、运行工程

http://127.0.0.1:8080/heap

出现内存溢出

jmap MAT内存溢出实践_spring_04

 

三、创建模拟非Heap内存溢出

1、引入asm



<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>


  

2、创建Metaspace类



/**

* 继承ClassLoader是为了方便调用defineClass方法,因为该方法的定义为protected
*/
public class Metaspace extends ClassLoader{

public static List<Class<?>> createClasses() {
// 类持有
List<Class<?>> classes = new ArrayList<Class<?>>();
// 循环1000w次生成1000w个不同的类。
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
// 定义一个类名称为Class{i},它的访问域为public,父类为java.lang.Object,不实现任何接口
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
"java/lang/Object", null);
// 定义构造函数<init>方法
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"()V", null, null);
// 第一个指令为加载this
mw.visitVarInsn(Opcodes.ALOAD, 0);
// 第二个指令为调用父类Object的构造函数
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
"<init>", "()V", false);
// 第三条指令为return
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();

Metaspace test = new Metaspace();
byte[] code = cw.toByteArray();
// 定义类
Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
classes.add(exampleClass);
}
return classes;
}

}


  

3、创建调用方式



private List<Class<?>>  classList = new ArrayList<>();


  



/**
* -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M
*
* @return
*/
@GetMapping("/nonheap")
public String nonheap(){
while (true){
classList.addAll(Metaspace.createClasses());
}
}


  

 4、设置JVM参数

-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M


jmap MAT内存溢出实践_spring_05

 

5、启动调用

http://127.0.0.1:8080/nonheap

 发现内存溢出

Exception in thread "http-nio-8080-exec-4" java.lang.OutOfMemoryError: Metaspace

 

 

四、解决方法

如果生成环境出现内存溢出,应该如何解决呢

 1、导出内存映像文件

1)内存溢出自动导出

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=./

配置如下

-Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

jmap MAT内存溢出实践_java_06

然后调用http://localhost:8080/heap

jmap MAT内存溢出实践_构造函数_07

显示hprof文件已经生成了

 

 

2)使用jmap命令手动导出

   jmap -dump:format=b,file=heap.hprof 15296

 jmap MAT内存溢出实践_构造函数_08

  15296是tomcat的进程。 这样就生成了一个heap.hprof 文件

 

jmap -heap 5579 查看内存的使用,每个区块占有多少内存(5579是tomcat的进程)

jmap MAT内存溢出实践_spring_09

 

2、MAT分析内存映像文件