【问题标题】:Memory leak of the substring method in javajava中子字符串方法的内存泄漏
【发布时间】:2014-10-26 10:56:47
【问题描述】:

我正在搜索由 java 中的 subString() 方法引起的内存泄漏。我在互联网上阅读了几篇文章并获得了一些知识,但最终我有点困惑。一篇文章提到了“String类内部的子字符串方法,调用String(int offset, int count, char value[])构造函数来创建新的String对象。这里有趣的是,value[],它与之前使用的字符数组相同。代表原始字符串”。这对我来说很清楚。

它还提到“如果原始字符串很长,并且具有大小为 1GB 的数组,那么无论子字符串多小,它都会容纳 1GB 数组。这也将阻止原始字符串被垃圾收集,如果没有任何实时参考”。

文章链接在这里 {http://javarevisited.blogspot.sg/2011/10/how-substring-in-java-works.html}

我无法理解这一点。请任何人解释一下,如果它没有任何实时引用,它如何阻止原始字符串被垃圾收集?

据我所知,在java中任何没有直接或间接引用的对象都可以被垃圾收集器收集。如果 subString 方法使用用于表示原始字符串的相同字符数组,原始字符串如何保留而不被垃圾收集?

同样我想知道内存泄漏是否是由于使用用于表示原始字符串的相同字符数组而引起的,因为与原始字符串相比,新字符串 {String 返回子字符串方法} 将非常少,或者因为原始字符串字符串仍然没有被垃圾收集吗?

请任何人帮助我澄清这一点

【问题讨论】:

    标签: java string


    【解决方案1】:

    当您在 Java 中分配任何内容时,在本例中为 String,只要您仍然可以访问它,它就会一直保留在内存中。如果你写

    String s = "blah";
    

    那么s 是该String引用。只要至少有一个对String 的引用,JVM 就知道它需要保存在内存中。一旦不再有引用,JVM 就可以清理它并在需要时回收内存。

    如果你写

    String s = "myverylongstring...somemore...someotherstuff";
    

    那么你已经用掉了相当多的内存。在内部,这将被表示为一个char[],其中包含您的String 的所有字符,边缘有一些额外的gubbins,以便您使用String API 访问它。

    现在,如果你写了会发生什么

    s = s.substring(20,30);
    

    所以s 现在只是对原始String 的一部分的引用? JVM 可以通过两种方式处理它。一种是将整个char[] 保存在内存中,但只需更改它准备让您访问的char[] 的哪一部分。另一种是将char[]的相关位复制到一个新数组中,然后去掉所有对旧数组的引用。

    第一种方法很快,因为它不需要任何复制。但是,如果您现在只想要大的String 的一小部分,那么整个大的String 永远无法完全清理,因为它的整个char[] 仍在内存中被使用。仅部分被使用的事实意味着它的内存没有可以被回收。

    第二种方法速度较慢,因为它需要一个复制操作。但这确实意味着之前的String 可以被完全清理掉,因为旧的char[] 不再被使用了。

    Java 使用 遵循第一种方法,但最近(Java 7 以后)它已切换到使用第二种方法。因此,内存泄漏曾经是 substring() 调用的问题,但现在不再存在了。

    【讨论】:

    • 非常感谢您的回复先生!根据您提到的第一种方式,这是否意味着它将原始字符串放入 char[] 数组并从 char[] 数组中获取位的一部分,我们可以访问那部分位,因为我们使用了subString 但数组中的其余位仍然可用并且垃圾收集器无法收集它,因为我们使用了 char[] 数组中的部分位?
    • @Dilan A Stringchar[] 和其他两个值表示:一个告诉您String 在数组中的哪个位置开始,另一个告诉您它有多长。因此,为了创建一个子字符串,我们可以创建一个新的String,它具有对相同char[] 的引用,但起点和长度不同。关键是通过将 reference 复制到 char[],我们避免了复制,但垃圾收集器现在需要保留整个 char[],即使我们只使用了一部分。
    【解决方案2】:

    措辞有点草率。原始的 String 实例将被垃圾收集。但是它的内部字符数组不会,因为它仍然被子字符串引用。

    在极端情况下,您可能有一个长度为 1 的 String 实例引用一个 1GB 数组。

    之前:

    s1 ---------> object --> char[] (1GB large)
                   - offset
                   - count
    

    子串被调用:

    s1 ---------> object --> char[] (1GB large)
                  - offset  ^
                  - count   |
                            |
    substring --> object ---/
                  - offset
                  - count
    

    s1 超出范围:对象可以被垃圾回收

                             char[] (1GB large)
                            ^
                            |
    substring --> object ---/
                  - offset
                  - count
    

    【讨论】:

    • 这不再是真的。这种行为在 Java 7 中有所改变。String 构造函数现在正在复制内部数组,因此原始的内部数组将被垃圾回收。
    • 我知道。但问题是关于解释以前存在的内存泄漏,当时这仍然是子字符串的实现方式。
    【解决方案3】:

    根据@chiastic-security 的回答,也可以找到这个参考来详细解释Java 8 处理内存泄漏的方法

    http://mrgyani.com/java-interview/memory-leak-problem-in-java-substring/

    【讨论】:

      猜你喜欢
      • 2013-12-03
      • 1970-01-01
      • 2011-08-29
      • 2011-08-29
      • 2014-04-30
      • 2013-03-14
      • 2013-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多