ObjectInputStream 所引起的 Java 内存泄漏问题

举报
fdsafdfas 发表于 2020/12/12 14:38:12 2020/12/12
【摘要】 Java 的 ObjectOutputStream 和 ObjectInputStream 各自保留一个对已发送/已接收对象的引用的列表。就是这些引用,会阻止垃圾收集器对这些对象内存的释放。当新对象的数量不断增长时(比方说在服务器中),最终将抛出"Java.lang.OutOfMemoryError"。解决办法就是使用 writeUnshared() 和 readUnshared() 方法来...

Java 的 ObjectOutputStream 和 ObjectInputStream 各自保留一个对已发送/已接收对象的引用的列表。就是这些引用,会阻止垃圾收集器对这些对象内存的释放。
当新对象的数量不断增长时(比方说在服务器中),最终将抛出"Java.lang.OutOfMemoryError"。解决办法就是使用 writeUnshared() 和 readUnshared() 方法来取代 writeObject() 和 readObject() 方法。
介绍
怎样在 Java 中创建一个内存泄漏?这是一个很好地面试题,因为 Java 编程中由于底层的自动垃圾收集功能而无需担心内存释放问题。但在复杂的情况下你仍然会遭遇 Java.lang.OutOfMemoryError 问题。这里就描述了一种这样的情况:由 java.io.ObjectInputStream 和 java.io.ObjectOutputStream 所实现的 Java 序列化机制所引起的内存泄漏。
原因
ObjectOutputStream 和 ObjectInputStream 都各自维护了一个对其已发送/已接收对象的引用表。所以当 ObjectOutputStream 重新发送某对象时,可以仍发送该对象的句柄,相应的 ObjectInputStream 则将接收到的句柄转换为先前接收到的对象的引用。Java 文档中这样描述:

使用共享机制 (…) 对单个对象的多个引用进行编码。

可以说,Java 的这一 feature 减少了带宽和内存的使用量,但也仅适用于通过链接定期重新发送对象的程序中。但在通过链接发送新对象的情况下,此功能除了不必要的(不断增长的)引用表以外没有任何作用。
垃圾收集器的工作原理就是清理不再被引用的对象。但由于 ObjectInputStream 始终保持了对其所接收的每个对象的引用,它阻止了垃圾收集器对通过流所接收的任何对象的清理。就在诸如服务器之类的程序中,它们接收到越来越多的对象,最终内存将被耗尽,引发 OutOfMemoryError。
情况识别和分析
OOM 可能有很多种不同的原因所造成。为了能够确定 OOM 是上文所讨论的 ObjectInputStream 相关 feature 所造成的,我们首先得来分析该进程的 heap dump。有很多种办法可以针对一个 Java 进程生成一个 heap dump,也有很多种办法来分析这些 dump 文件。我们可以通过将 JVM 命令行选项添加到进程执行命令行来生成 dump。还可以针对一个已经在运行中的程序来生成一个 dump。其中,通过添加命令行选项的方式可以在有 OOM 错误时生成 dump 文件(当然,还有一些其他命令行选项通过信号控制方式来在任意时间生成 dump,不管程序状态如何)。命令行选项如下:

-XX:+HeapDumpOnOutOfMemoryError
  • 1

在 OOM 抛出时,一个名为 java_pid.hprof 的文件将会在该进程的执行目录所生成。有 n 多 dump 分析程序可以对 dump 文件进行分析,本文只对 jvm 原生自带的 VisualVM 进行介绍。
在 VisualVM 中,打开你收集到的 hprof 文件。在 classes 选项卡中,可以找到所有的类,这些类默认是按照 dump 中所存在的它们所拥有的实例数进行排序。当然,顶部的类极可能就是实例数暴多且导致内存错误的那个类。按字母顺序对类列表进行排序也是很有帮助的,这样可以将同一包的类分组在一起,进而可以对它们进行比较。
双击该列表中的一个类,视图切换至 instances 选项卡。对于每个实例,都有两个窗口。下边的那个保存指向该实例的参考路径。每个路径的边缘是一个垃圾收集 root(由三角形标记)。这样每个边缘点都是一个对象,该对象持有一个引用,防止 GC 对该对象的清理。如果很多这种爆炸的类实例都指向了一个 ObjectInputStream 对象,那么导致 OOM 的罪魁祸首可能就是本文所讨论的主题。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。