程序运行 19 分 06 秒后,发生 OOM 异常:
那正常的走势图应该是怎么样的呢?
我们在 JDK 1.8.0_121 版本中(已经修复了 remove 方法),用相同的 JVM 参数(-Xmx20m)再跑一下:
首先从上面的日志中可以看出,时间间隔并没有递增,程序运行的非常的快。
然后用 VisualVM 检测内存,同样跑 19 分钟后截图如下:
可以看到堆内存的使用量并没有随着时间的推移而越来越高。但是还是有非常频繁的 GC 操作。
这个不难理解,因为 CLQ 的数据结构用的是链表。而链表又是由不同的 node 节点组成。
由于调用 remove 方法后,node 节点具备被回收的条件,所以频繁的调用 remove 方法对节点进行删除,会触发 JVM 的 min GC。
这种 JDK BUG 导致的内存泄漏其实挺让人崩溃的。首先你第一次感知到它是因为程序发生了 OOM。
也许你会先无脑的加大堆内存空间,恰好你的程序运行了一周之后又要上线了,所以涉及到重启应用。
然后很长一段时间内没有发生 OOM 了。你就想这个问题可能解决了。
但是它还是在继续发生着,很可能由于节假日前后不能上线,比如国庆七天,加上前后几天,大概有半个月的样子应用没有上线,所以没有重启,程序越来越慢,最终导致第二次 OOM 的出现。
这个时候,你觉得可能不是内存溢出这么简单了。
会不会是内存泄漏了?
然后你再次重启。这次重启之后,你开始时不时的 Dump 一下内存,拿出来分析分析。
突然发现,这个 node 怎么这么多呢?
最终,找到这个问题的原因。
原来是 JDK 的 BUG。
你就会发出和 Greg 一样的感叹:卧槽,震惊,这么牛皮!?
我这个运行了 60 多小时的程序到现在堆内存使用了 233m,但是我整个堆的大小是接近 2G。
通过 jmc 同时展示堆的整体大小和已经使用的堆大小你可以发现,距离内存泄漏可以说是道阻且长了:
我粗略的算了一下,这个程序大概还得运行 475 个小时左右,也就是 19 天之后才会出现由于内存泄漏,导致的 OOM。
我会尽量跑下去,但是听到我电脑嗡嗡嗡的风扇声,我不知道它还能不能顶得住。
如果它顶住了,我在后面的文章里面通知大家。
好了,图形化工具这一小节就到这里了。
我们只是展示了它们非常小的一个功能,合理的使用它们常常能达到事半功倍的作用。
如果你不太了解它们的功能,建议你看看《深入理解JVM虚拟机(第3版)》,里面有一章节专门讲这几个工具的。
最后说一句(求关注)
这是我昨天晚上写文章的时候拍的 ,女朋友说一眼望去感觉我是一个盯盘的人,在看股票走势图,这只股票太牛逼了。
要是股市的总体走势也像内存泄露那么单纯而直接就好了。
只要在 OOM 之前落袋为安就行。可惜有的人就是在 OOM 的前一刻满仓杀入,真是个悲伤的故事。
文中提到的两本书,都是非常优秀的值得学习的书籍。作为一个 Java 程序员,如果你还没有拥有这两本书,我强烈建议你买来看看。
买不了吃亏,买不了上当,只会觉得相见恨晚。你会发现原来这么多 JVM、多线程相关的面试题都是出自这两本书:
才疏学浅,难免会有纰漏,如果你发现了错误的地方,还请你留言指出来,我对其加以修改。
感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。
我是 why,一个被代码耽误的文学创作者,不是大佬,但是喜欢分享,是一个又暖又有料的四川好男人。