上一篇问题讲了怎么实现jvm内存溢出,现在已经实现了,那怎么去解决它呢.
java.lang.OutOfMemoryError: GC overhead limit exceeded
简单来说,java.lang.OutOfMemoryError: GC overhead limit exceeded
发生的原因是,当前已经没有可用内存,经过多次 GC 之后仍然没能有效释放内存。
众所周知,JVM 的 GC 过程会因为 STW,只不过停顿短到不容易感知。当引起停顿时间的 98%都是在进行 GC,但是结果只能得到小于 2%的堆内存恢复时,就会抛出java.lang.OutOfMemoryError: GC overhead limit exceeded
这个错误。Plumbr给出一个示意图:
JVM 给出一个参数避免这个错误:-XX:-UseGCOverheadLimit
。
但是,这个参数并不是解决了内存不足的问题,只是将错误发生时间延后,并且替换成java.lang.OutOfMemoryError: Java heap space
。
还有一个偷懒的方法是:增大堆内存。既然堆内存少了,那就增加堆内存即可。
-Xms2048m -Xmx2048m
但是,这个方法也不是万能的。因为程序里可能有内存泄露。这个时候即使再增大堆内存,也会有用完的时候。
所以前两个方法都只是治标不治本而已。
其实还是有一个终极方法的,而且是治标治本的方法,就是找到占用内存大的地方,把代码优化了,就不会出现这个问题了。
怎么找到需要优化的代码呢?就是通过 heap dump 生产 jvm 快照,通过分析快照找到占用内存大的对象,从而找到代码位置。
通过设置-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump
参数来生产快照,然后通过 VisualVM 或者MAT等工具分析快照内容进行定位。通过这个参数是将发生 OOM 时的堆内存所有信息写入快照文件,也就是说,如果此时堆内存中有敏感信息的话,那就可能造成信息泄漏了。
可以用JDK1.8自带的jvisualvm工具分析,个人感觉不太好用
所以我选择用MAT工具
下载并安装MAT工具百度有,这里不做介绍
在本地spring项目启动配置两个参数
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump
然后用postman访问接口bbb/bbb01接口,死循环往userlist里面不停塞user对象.
最终导致报内存溢出错误.
停止这个服务.就会在项目的文件夹中自动生成一个heapdump文件.
打开MAT,开始下面5个步骤.就打开了heapdump文件.
(注意,第一次下载mat后直接打不开heapdump文件,需要重启一下电脑才能打开heapdump文件)
shallow 浅的 retained 保留的 heap 堆 percentage 百分率
dominator_tree 支配树(查看占大内存的类,以及类里面含的对象) 常用这个
histogram 柱形统计图(查看占大内存的对象)
shallow heap:对象本身的大小,如果是数组或集合则是各个元素的总大小。
retained heap:对象本身的大小 + 引用的其他对象的大小。
Shallow Size (对象自身占用的内存大小)
Retained Size (被GC后Heap上释放的内存大小)
with outgoing references(查看对象为什么消耗内存,查看对象引用的其他对象)
with incoming references(查看对象被谁引用)
发现了JVMneicunyichuController这个类占用内存非常大,然后点开发现原来是Arraylist里面有非常多的User对象
去代码中找JVMneicunyichuController这个类的ArrayList集合中为什么会有这么多User对象.
找到这个类的集合后发现原来是while死循环往集合里加User对象了.
把这个死循环去掉,那么,这个内存溢出的问题就解决了.
方式一:点击Histogram,查看占大内存的对象.
还有其他方式,可以自己百度了解.