这里我们不讲JVM的内存划分,垃圾判定算法,垃圾回收算法,垃圾收集器等知识。主要讲的是实际调优的操作,对JVM调优感兴趣的可以看下去。至于垃圾回收算法,可以看看我这篇文章:
“
https://mp.weixin.qq.com/s?__biz=Mzg4NDMyNTIwMw==&mid=2247487158&idx=1&sn=4a36bf8eec50b584a1ac08069e32afeb&chksm=cfb8a08af8cf299c089ff889c6a42ba18bbaccd054135d0c78eeb69bfda6f28796c3ba9d6625&token=150423897&lang=zh_CN&scene=21#wechat_redirect
”
公司系统出现内存溢出的故障,下面是内存溢出排除过程,我采用伪代码模拟了生产环境。
public class MemoryLeakService { public List distinct() throws InterruptedException { List list = new ArrayList<>(); CountDownLatch downLatch = new CountDownLatch(1); new Thread(() -> { try { for (int j = 0; j
这里主要是模拟一个缓存加载的过程,将用户数据加载进List集合中。为了体现效果,我们将堆内存调小,并将内存溢出的堆栈信息打印出来,具体指令如下:
-Xmx8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/temp/20200824.hprof
当运行系统时,出现如下报错
![004a3b2101501796dc1bee5d596c3547.png](https://img-blog.csdnimg.cn/img_convert/004a3b2101501796dc1bee5d596c3547.png)
我们发现Thread-1的堆空间内存溢出了,并且生成如下文件20200824.hprof。
![40ed17c749e55692a3732d490cdf3b94.png](https://img-blog.csdnimg.cn/img_convert/40ed17c749e55692a3732d490cdf3b94.png)
打开内存分析工具 Eclipse Memory Analyzer
![1ddcf141b7fa92ae2964b7eafea9854a.png](https://img-blog.csdnimg.cn/img_convert/1ddcf141b7fa92ae2964b7eafea9854a.png)
加载20200824.hprof文件,找到有问题的堆栈信息
![c70e41330905704ffafe84b78f9f81d7.png](https://img-blog.csdnimg.cn/img_convert/c70e41330905704ffafe84b78f9f81d7.png)
控制台打印显示Thread-1由内存溢出,我们进Thread-1看
![a36e6382a422844493f1bd18712a2be9.png](https://img-blog.csdnimg.cn/img_convert/a36e6382a422844493f1bd18712a2be9.png)
点击箭头处按钮
![3053b0027be9d96e92140f5776c86827.png](https://img-blog.csdnimg.cn/img_convert/3053b0027be9d96e92140f5776c86827.png)
选择线程明细
![51254b940ad3f4bf332a5a9b7fcdb588.png](https://img-blog.csdnimg.cn/img_convert/51254b940ad3f4bf332a5a9b7fcdb588.png)
![a861e36fd353eef756dab4f447989533.png](https://img-blog.csdnimg.cn/img_convert/a861e36fd353eef756dab4f447989533.png)
我们发现MemoryLeakService第24行代码有问题,看看第24行代码
![a8c0c1c7f3d46a490fa1774e5a9762db.png](https://img-blog.csdnimg.cn/img_convert/a8c0c1c7f3d46a490fa1774e5a9762db.png)
是这个对象导致的。
仔细看代码,发现User对象存入了List集合中
if (!list.contains(user)) { list.add(user);}
看看contains()源码
![d1838f96435b316bbc254b226c9ee619.png](https://img-blog.csdnimg.cn/img_convert/d1838f96435b316bbc254b226c9ee619.png)
发现这里是比较的地址值,那!list.contains(user)永远为true。这里就相当于List存了个User。
我们需要重写User的equals方法
@Overridepublic boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User user = (User) o; return Objects.equals(id, user.id);}@Overridepublic int hashCode() { return Objects.hash(id, name, age);}
再次运行,没有出现内存溢出了。
![4c3cd654e8d2e2cffae3dc97e51112ed.png](https://img-blog.csdnimg.cn/img_convert/4c3cd654e8d2e2cffae3dc97e51112ed.png)
好了,上面就是一次简单的内存溢出查找的过程了,关于工具Eclipse Memory Analyzer的使用,自己可以去网上下载下来,练习使用下。说不定哪天你们的系统真的出现内存溢出,自己就有用武之地了。