打通JVM调优第三战-堆内存溢出排查与分析

笔者有话说

纸上得来终觉浅,绝知此事要躬行。

这句话笔者一直把它当做自己的座右铭。

希望大家也能抱有和笔者一样的姿态,多动手,多实践。让我们用实际行动证明,在我们年轻人的世界,不存在‘不可能’。

聊聊内存溢出

内存溢出是实际项目中经常会遇到的问题,关于内存溢出,可以细分为:

  • 堆内存溢出
  • 栈内存溢出
  • 方法区溢出
  • 直接内存溢出

这四类。

本文,我们就主要探讨一下堆内存溢出问题。
看我下面演示一段容易造成OOM的代码:

在这里插入图片描述
这段代码很简单,在main方法里面,我们不断地往oomList里面添加元素,然后使用我注释中的参数启动一下。
在这里插入图片描述

这里我们把最小堆内存以及最大堆内存都设置为20M。然后我们使用HeapDumpOnOutOfMemoryError这个参数,让应用在OutOfMemoryError发生的时候做一次堆Dump。

启动后会发现报错了:
在这里插入图片描述

错误信息很明确,堆内存溢出。
我们在项目根目录可以找到一个生成的堆Dump文件。

在这里插入图片描述
我们用内存分析工具VisualVM或者MAT分析一下。

VisualVM定位分析

先来演示一下VisualVM.
在这里插入图片描述
加载生成的堆Dump文件
在这里插入图片描述
我们可以看到占用空间最大的实例是一个Object数组,然后我们右击 Open in New Tab
在这里插入图片描述
可以看到是elementData引用了这个数组
在这里插入图片描述
再展开,可以看到是oomList引用了elementData
在这里插入图片描述
这里我们就可以推断是oomList存在问题。

MAT定位分析

接下来再看下一款工具来分析堆Dump
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看到,在整个堆里面,Thread这个对象占用了15.6MB,这显然是不正常的,我们点击Leak Suspects,分析内存泄露
在这里插入图片描述
在这里插入图片描述
可以看到,这里描述这个main线程占用了92.21%的空间。我们点击Details查看一下详情:
在这里插入图片描述
在这里插入图片描述
我们可以看到在这下方有个支配数中积累的对象,我们可以看到对象都是在Object数组里面堆积的。
我们点进去看看这个数组被谁引用了
在这里插入图片描述
在这里插入图片描述
逐个展开后我们又定位到了oomList这个对象,断定是oomList导致的堆内存溢出。

此外,我们还可以在default_report栏中找到See stacktrace,查看调用栈信息。

在这里插入图片描述
在这里插入图片描述
从这个调用栈里我们也可以找到到底哪一行代码出了问题。

总结

经过实验,我们不难发现,借助工具定位堆内存溢出的问题还是比较轻松的,当定位出问题后,我们就能结合我们业务进行相应改造了。
一般来说,导致堆内存溢出有两种场景:

  • 内存泄露
    这种情况下,我们应该借助工具去查看泄漏对象到对象的引用链,找到这个泄漏的对象是通过怎样的路径跟哪个对象关联,从而导致的这个对象没有被回收,只要能准确定位到泄漏对象的创建位置,就能迅速找到导致溢出的代码了。
  • 非内存泄漏
    这种情况就是,你的项目并不存在内存泄漏的问题,也就是说,经过分析,目前内存里面的对象,都是必须存活的,那这个时候我们应该根据机器的配置,调大Xms、Xmx,为应用分配更多的堆内存。或者你可以检查一下你的代码,看下是不是有些对象生命周期太长,又或者存储结构不合理。

猜你喜欢

转载自blog.csdn.net/qq_45455361/article/details/120969252#comments_25407191
今日推荐