一个挺有意思的OOM Case

上周有个应用出现了OOM,但现象还挺有意思的,非常与众不同,分享下。

当时出问题的时候现象为应用的日志里打了这样的日志:
allocating large array…
array_size[1073741848 bytes]
array_length[1073741826 elememts]
这个在之前阿里版JDK里有说过是阿里版JDK的增强功能,当运行时有地方出现创建一个巨大的数组(例如>700MB)时,会直接打印出这样的日志,并同时会输出相应的线程堆栈信息。
如果不是阿里版的JDK,在这种情况下看到的现象就会是OOM的异常信息,那样排查会稍微麻烦一些。

线程堆栈信息如下:
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
– locked <0x0000000799b41f58> (a java.io.ByteArrayOutputStream)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1847)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1756)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1169)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at java.util.HashMap.writeObject(HashMap.java:1000)
at sun.reflect.GeneratedMethodAccessor134.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:940)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)

从这个线程堆栈信息来看,是在序列化一个HashMap时写了一个巨大的数组。

逻辑上来说,造成这样的现象,应该是HashMap本身非常大,因此就去查看了下当时dump下来的内存信息,看看当时在序列化的这个HashMap的状况。

在MAT中查看,发现这个HashMap中的一个桶里的链表出现了头尾是同一个对象的现象,按照经验,出现这种现象的原因是在并发场景中使用了HashMap进行读写,并且没做线程安全的保护。

和业务方确认后确定确实这个HashMap被用在了并发场景,并且没做保护,而这个HashMap又是要作为结果对象返回给调用端的,所以导致在序列化这个HashMap的时候出现了HashMap.writeObject一直结束不了,并且会积累出很大的数组,从而引发了上面的异常信息。

=============================
题图来源于:http://goo.gl/9QHMLe
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

《一个挺有意思的OOM Case》有1个想法

  1. 遍历这个坏桶的时候死循环了,直到触发阿里的JVM告警。

    不知道阿里的JVM有没有死循环检测功能?有的话,也许会先触发死循环告警。

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*