Java 标准 API 中的内存泄漏陷阱

Posted

技术标签:

【中文标题】Java 标准 API 中的内存泄漏陷阱【英文标题】:Memory leak traps in the Java Standard API 【发布时间】:2010-11-19 20:58:28 【问题描述】:

Java 标准 API 的哪些类在以(不是很明显)不正确的方式使用时会导致内存泄漏?以及如何避免/修复这些内存泄漏?

示例: ObjectInputStreamObjectOutputStream 保留对他们看到的所有对象的引用,以便将同一对象的后续出现作为引用而不是副本发送(从而处理循环引用) .当您无限期地保持此类流打开时(例如,当使用它通过网络进行通信时),这会导致内存泄漏。

修复:定期或在每个***对象之后调用 reset()。

【问题讨论】:

@Michael - 也许将您的示例单独移至答案? 【参考方案1】:

一个很大的问题是获取Java字符串的子字符串是指原始字符串。

示例:您读取 3000 个字符的记录并获得一个 12 个字符的子字符串,然后将其返回给调用者(在同一个 JVM 中)。即使您没有直接引用原始字符串,那 12 个字符的字符串仍在内存中使用 3000 个字符。

对于接收然后解析大量消息的系统,这可能是一个真正的问题。

您有几种方法可以避免这种情况:

String sub = new String(str.substring(6,12));

String sub = str.substring(6,12).intern();

第一个更明确。第二个有其他含义,因为您使用的是 PermGen 空间。过度使用你可能会用完,除非你给你的虚拟机足够多。

请记住,这仅在您使用小子字符串然后丢弃原始字符串并且您经常这样做时才相关。

【讨论】:

提一个简单的修复方法,比如显式创建一个新的 String 对象怎么样? @Murat:碰到一个旧评论:因为它在内部引用了与原始字符串相同的 char 数组,只是偏移量和长度不同。如果你创建了很多重叠的子字符串,它实际上会节省内存。 +1:我想说这是 JDK 中最大的“陷阱”之一……我花了很长时间才确定这是我的网络爬虫泄露整个文件的原因它看到的每个页面的 html 源代码。说真的,Sun/Oracle ......如果你要产生如此严重的“优化”副作用,至少在 the documentation 中说一下 哦,更糟糕的是,每当您在内部使用基于 substring() 的其他 API(例如 java.util.regex 中的内容)时,它都会打击您。 我查看了String#substring的源代码,它将创建一个新的String并复制一个内部char[]的范围。我正在使用jdk7,也许这是固定的?【参考方案2】:

您注册为事件接收者的所有内容,例如在 GUI 框架中, 只要注册过并且事件源就不能被垃圾回收 还活着。

如果开发人员没有意识到这一点,这可能会导致内存泄漏 从事件源到事件订阅者的强引用。

【讨论】:

对于预期寿命比正在收听的内容更长的任何听众,最好使用弱引用。 这几乎是内存泄漏的标准示例,因为它很明显但很容易忘记。【参考方案3】:

您为外部类设置的任何非静态内部类。这样看起来无辜的内部类就可以持有一个巨大的对象图。将实例放在某个静态或应用程序范围的集合中,您正在使用大量您不应该使用的内存。

【讨论】:

【参考方案4】:

线程的每个实例化都会为堆栈分配内存(默认为 512k,可通过-Xss 调整)。这不是泄漏本身,而是天真地尝试对应用程序进行大量多线程将导致相当大的内存消耗。

【讨论】:

实际上,如果您创建 Thread 对象并且从不调用 start(),它可能会变成彻底的泄漏 - 然后堆栈内存永远不会回收,IIRC。 有趣。不知道。 @Michael Borgwardt:我不认为是这样。你有资源吗? [编辑]:看起来它可能是旧版本 JVM 中的一个错误,但应该在 Java 5 中修复。【参考方案5】:

任何具有 dispose() 方法的类?

例如java.awt.Window#dispose:

公共无效处置()

释放此 Window、其子组件及其所有子组件使用的所有本机屏幕资源。也就是说,这些组件的资源将被销毁,它们消耗的任何内存都将返回给操作系统,并且它们将被标记为不可显示。

【讨论】:

【参考方案6】:

当弱引用已经足够时使用强引用。执行自己的状态和资源管理的应用程序和 API 是常见的罪魁祸首。

然后是观察者模式的使用,这可能导致内存泄漏——当观察者将自己从主题中删除时,除非主题也释放对观察者/监听者的引用,否则内存无法回收。前面已经指出了这一点,但没有多少人意识到即使是记录者也是观察者。

另外,there is the likelihood of classes, whose objects when instantiated are automatically placed into a static member。其中一些类甚至没有 release() 或 dispose() 方法,因此引用继续由静态成员持有。这种类型的内存泄漏最终会导致 OutOfMemory 错误,并将 PermGen 空间问题报告为根本原因,从而使其更难诊断。

【讨论】:

以上是关于Java 标准 API 中的内存泄漏陷阱的主要内容,如果未能解决你的问题,请参考以下文章

如何防止java中的内存泄漏

转C 语言中的指针和内存泄漏

netty API 中的内存泄漏

Java中的内存泄漏问题

Java中的内存泄漏

Web API 2.0 方法中的内存泄漏