16

据说substring String类中的方法会导致内存泄漏。这是真的吗?如何?它有什么替代方案?
特别是在寻找答案,
还有哪些其他可能导致 Java 内存泄漏的事情?这将帮助我在编码时小心。

4

4 回答 4

31

在过去的 JDK 版本中,该substring方法的实现将构建一个新String对象来保存对整个 char 数组的引用,以避免复制它。因此,您可能会在不经意间保留对只有一个字符串的非常大的字符数组的引用。这是一个可能引发的错误示例。

这种方法现在已经改变,这种“泄漏”不再存在。

如果您想使用旧的 JDK(比 OpenJDK 7、Update 6 更早)并且希望在 之后具有最少的字符串substring,请使用构造函数采用另一个字符串:

String s2 = new String(s1.substring(0,1));

至于你的第二个问题,关于“其他可能导致 java 内存泄漏的事情”,不可能以建设性的方式回答。在 java 标准库中,您可以轻松地保留对对象的隐藏引用的情况下,并没有很多实例。在一般情况下,请注意您构建的所有引用,最常见的问题可能出现在未清理的集合或外部资源(文件、数据库事务、本机小部件等)中。

于 2013-03-25T10:04:31.280 回答
12

substring()方法不会为 a 分配新的字符数组String,而是简单地在现有的char 数组上生成String带有窗口的 a 。这是享元模式的实施,被认为是一种优化。

因此,如果我有一个巨大的String(char 数组)然后创建一个子字符串,即使我垃圾收集了原始字符串,原始 char 数组仍然存在(尽管您认为您有一个子字符串,例如 2 个字符)。当(例如)解析大量输入数据(可能是 XML 文件)并通过以下方式提取少量文本时,经常会遇到此问题substring()

使用看似多余String(String str)的构造函数(String构造函数采用String!)解决了这个问题,因为它分配了一个新的(可能更小)的 char 数组,允许对原始数组进行垃圾收集。

请注意,此行为自 Java 7u6 起已更改。

于 2013-03-25T10:05:07.533 回答
8

字符串子字符串可能会导致保留比您预期更多的内存。因此,这不是内存泄漏,因为此内存可以正常恢复。

最简单的解决方案是使用最新版本的 Java 7,它不这样做。由于这是 Oracle 唯一免费支持的版本,因此无论如何您都应该考虑这样做。

因此,它在 Java 7 更新 5 中已“修复”。恕我直言,与其说是修复不如说是简化了实现。获取每个子字符串的副本需要更多的工作,并且可能会消耗更多的内存,但这确实意味着少了一件需要担心的事情。

有哪些其他因素会导致 Java 中的内存泄漏?

任何对象都可以被清理,因此不可能在 C/C++ 术语的意义上创建内存泄漏。你可以做的是错误地抓住物体。一个常见的例子是忘记关闭像 JDBC 资源这样的资源。这可能会导致您以意想不到的方式保留记忆。

于 2013-03-25T10:04:57.553 回答
5

在 String 对象中,当您调用 时substring,该value属性在两个字符串之间共享。

所以,如果你从一个大字符串中得到一个子字符串并保存很长时间,那么大字符串就不会被垃圾回收。实际上,它可能会导致内存泄漏。

于 2013-03-25T10:05:55.977 回答