1-报错信息

java.lang.OutOfMemoryError: Java heap space

2-案例模拟

用spring boot创建的案例

  1. package com.atguigu.demo.controller;
  2. import com.atguigu.demo.bean.People;
  3. import com.atguigu.demo.service.PeopleSevice;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.cglib.proxy.*;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import java.lang.management.ClassLoadingMXBean;
  9. import java.lang.management.ManagementFactory;
  10. import java.lang.reflect.Method;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import java.util.concurrent.ArrayBlockingQueue;
  14. import java.util.concurrent.ThreadPoolExecutor;
  15. import java.util.concurrent.TimeUnit;
  16. /**
  17. * <pre>
  18. * @author : shkstart
  19. * time : 08:54
  20. * desc : 内存测试
  21. * version : v1.0
  22. * </pre>
  23. */
  24. @RestController
  25. public class MemoryTestController {
  26. @Autowired
  27. private PeopleSevice peopleSevice;
  28. /**
  29. * 案例1:模拟线上环境OOM
  30. */
  31. @RequestMapping("/add")
  32. public void addObject(){
  33. System.err.println("add"+peopleSevice);
  34. ArrayList<People> people = new ArrayList<>();
  35. while (true){
  36. people.add(new People());
  37. }
  38. }
  39. /**
  40. * 案例2:模拟元空间OOM溢出
  41. */
  42. @RequestMapping("/metaSpaceOom")
  43. public void metaSpaceOom(){
  44. ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
  45. while (true){
  46. Enhancer enhancer = new Enhancer();
  47. enhancer.setSuperclass(People.class);
  48. // enhancer.setUseCache(false);
  49. enhancer.setUseCache(true);
  50. enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
  51. System.out.println("我是加强类,输出print之前的加强方法");
  52. return methodProxy.invokeSuper(o,objects);
  53. });
  54. People people = (People)enhancer.create();
  55. people.print();
  56. System.out.println(people.getClass());
  57. System.out.println("totalClass:" + classLoadingMXBean.getTotalLoadedClassCount());
  58. System.out.println("activeClass:" + classLoadingMXBean.getLoadedClassCount());
  59. System.out.println("unloadedClass:" + classLoadingMXBean.getUnloadedClassCount());
  60. }
  61. }
  62. /**
  63. * 性能优化案例3:合理配置堆内存
  64. */
  65. @RequestMapping("/getData")
  66. public List<People> getProduct(){
  67. List<People> peopleList = peopleSevice.getPeopleList();
  68. return peopleList;
  69. }
  70. }

发送请求: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 文件。如下图所示:
image.png
还可以进一步分析log日志。见生成的log日志即可。

5-原因及解决方案

原因

1、代码中可能存在大对象分配
2、可能存在内存泄漏,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。

解决方法

1、检查是否存在大对象的分配,最有可能的是大数组分配
2、通过jmap命令,把堆内存dump下来,使用MAT等工具分析一下,检查是否存在内存泄漏的问题
3、如果没有找到明显的内存泄漏,使用 -Xmx 加大堆内存
4、还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性

6-dump文件分析

jvisualvm分析

  • 接下来我们使用工具打开该文件,由于我们当前设置的内存比较小,所以该文件比较小,但是正常在线上环境,该文件是比较大的,通常是以G为单位。
  • jvisualvm工具分析堆内存文件heapdump.hprof:

    image.png
    image.png
    通过jvisualvm工具查看,占用最多实例的类是哪个,这样就可以定位到我们的问题所在。
    image.png

    MAT分析

  • 使用MAT工具查看,能找到对应的线程及相应线程中对应实例的位置和代码:

image.png

7-gc日志分析

image.png