1-报错信息
java.lang.OutOfMemoryError: Java heap space
2-案例模拟
用spring boot创建的案例
package com.atguigu.demo.controller;
import com.atguigu.demo.bean.People;
import com.atguigu.demo.service.PeopleSevice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* <pre>
* @author : shkstart
* time : 08:54
* desc : 内存测试
* version : v1.0
* </pre>
*/
@RestController
public class MemoryTestController {
@Autowired
private PeopleSevice peopleSevice;
/**
* 案例1:模拟线上环境OOM
*/
@RequestMapping("/add")
public void addObject(){
System.err.println("add"+peopleSevice);
ArrayList<People> people = new ArrayList<>();
while (true){
people.add(new People());
}
}
/**
* 案例2:模拟元空间OOM溢出
*/
@RequestMapping("/metaSpaceOom")
public void metaSpaceOom(){
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
while (true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(People.class);
// enhancer.setUseCache(false);
enhancer.setUseCache(true);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("我是加强类,输出print之前的加强方法");
return methodProxy.invokeSuper(o,objects);
});
People people = (People)enhancer.create();
people.print();
System.out.println(people.getClass());
System.out.println("totalClass:" + classLoadingMXBean.getTotalLoadedClassCount());
System.out.println("activeClass:" + classLoadingMXBean.getLoadedClassCount());
System.out.println("unloadedClass:" + classLoadingMXBean.getUnloadedClassCount());
}
}
/**
* 性能优化案例3:合理配置堆内存
*/
@RequestMapping("/getData")
public List<People> getProduct(){
List<People> peopleList = peopleSevice.getPeopleList();
return peopleList;
}
}
发送请求:http://localhost:8080/add
3-JVM参数配置
参数配置: 初始-Xms30M -Xmx30M
-XX:+PrintGCDetails -XX:MetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof -XX:+PrintGCDateStamps -Xms200M -Xmx200M -Xloggc:log/gc-oomHeap.log
4-运行结果
运行结果:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210) ~[na:1.8.0_131]
at java.util.Arrays.copyOf(Arrays.java:3181) ~[na:1.8.0_131]
at java.util.ArrayList.grow(ArrayList.java:261) ~[na:1.8.0_131]
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) ~[na:1.8.0_131]
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) ~[na:1.8.0_131]
运行程序得到 heapdump.hprof 文件。如下图所示:
还可以进一步分析log日志。见生成的log日志即可。
5-原因及解决方案
原因
1、代码中可能存在大对象分配
2、可能存在内存泄漏,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。
解决方法
1、检查是否存在大对象的分配,最有可能的是大数组分配
2、通过jmap命令,把堆内存dump下来,使用MAT等工具分析一下,检查是否存在内存泄漏的问题
3、如果没有找到明显的内存泄漏,使用 -Xmx 加大堆内存
4、还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性
6-dump文件分析
jvisualvm分析
- 接下来我们使用工具打开该文件,由于我们当前设置的内存比较小,所以该文件比较小,但是正常在线上环境,该文件是比较大的,通常是以G为单位。
jvisualvm工具分析堆内存文件heapdump.hprof:
通过jvisualvm工具查看,占用最多实例的类是哪个,这样就可以定位到我们的问题所在。
MAT分析
使用MAT工具查看,能找到对应的线程及相应线程中对应实例的位置和代码: