Java String.substring方法是否存在潜在内存泄漏?

Java String.substring方法是否存在潜在内存泄漏?,java,string,memory-leaks,Java,String,Memory Leaks,我正在浏览字符串类API,看起来子字符串方法可能导致内存泄漏,因为它与原始字符串共享相同的字符数组 若原始字符串很大,那个么子字符串返回的小字符串可以防止原始字符串(由大数组备份)在Java中被垃圾收集 任何想法或我是否读错了API。如果您获取一个相当大的字符串的子字符串而不复制(通常通过字符串(字符串)构造函数),则可能会发生内存泄漏 请注意,这一点在之后发生了变化。 看 关于字符串对象实现的原始假设不再被视为有效 有关更多信息,请参阅 在Java 7u6之前,情况一直如此-您通常会通过以下方

我正在浏览字符串类API,看起来子字符串方法可能导致内存泄漏,因为它与原始字符串共享相同的字符数组

若原始字符串很大,那个么子字符串返回的小字符串可以防止原始字符串(由大数组备份)在Java中被垃圾收集

任何想法或我是否读错了API。

如果您获取一个相当大的字符串的子字符串而不复制(通常通过
字符串(字符串)
构造函数),则可能会发生内存泄漏

请注意,这一点在之后发生了变化。 看

关于
字符串
对象实现的原始假设不再被视为有效

有关更多信息,请参阅

  • 在Java 7u6之前,情况一直如此-您通常会通过以下方式处理此问题:

    String sub = new String(s.substring(...)); // create a new string
    
    这将有效地删除依赖项,原始字符串现在可用于GC。顺便说一下,这是使用字符串构造函数有意义的唯一场景之一

  • 因为,创建了一个新字符串,不再存在内存问题


  • 在Java 7中,字符串的子字符串修改为:

    /**
         * Returns a new string that is a substring of this string. The
         * substring begins with the character at the specified index and
         * extends to the end of this string. <p>
         * Examples:
         * <blockquote><pre>
         * "unhappy".substring(2) returns "happy"
         * "Harbison".substring(3) returns "bison"
         * "emptiness".substring(9) returns "" (an empty string)
         * </pre></blockquote>
         *
         * @param      beginIndex   the beginning index, inclusive.
         * @return     the specified substring.
         * @exception  IndexOutOfBoundsException  if
         *             <code>beginIndex</code> is negative or larger than the
         *             length of this <code>String</code> object.
         */
        public String substring(int beginIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            int subLen = value.length - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
        }
    

    因此,每次使用beginIndex不等于0的子字符串时,我们都会有一个新的字符串对象。

    从技术上讲,这根本不是内存泄漏,因为字符数组仍然被引用,并且可以在以后收集引用它的所有字符串时被收集。字符数组的一部分可能不再使用,但这并不构成泄漏。如果每100MB有一个100个大字符串,并且有一个子字符串(0,1),则从技术上讲,您持有字符串类中使用的值[],并且在应用程序中决不允许使用大字符串进行垃圾回收最佳链接这两种策略都是有效的。最初的impl很好;新的impl很好。但是,改变impl是不好的。。。。这是对兼容性的惊人突破。因为显而易见的原因,他们不可能做到这一点。还有一些事情发生了。@unjutable这是一个不属于规范的实现细节=>不应该依赖它。更改是出于性能原因。@assylias这更像是一种逃避。人们已经相信substring()/trim()是O(1)。悄悄地把它改成O(n)不是很好。那么,如何在不同的Java版本上编写行为一致的代码呢?他们这样做是出于性能原因,但不是明显的原因,这些原因非常弱。@unreputable他们这样做是为了提高字符串的性能,但缺点是子字符串的性能已经恶化……是的,但这会产生新的问题。如果您
    trim()
    去掉一个空白,这是一种非常常见的情况,您最终会复制
    N-1
    字符。@unreputable您总是可以找到性能更差的角落。通用iLibrary的目标是平均性能良好,在进行更改之前,似乎已经考虑了许多不同的用例。此外,复制字符非常快(不是说没有成本)。
    trim()
    并不是一个特例。实际上,
    str.substring().trim()
    是一种非常常见的情况,需要复制两次<代码>O(1)->O(n)是一件大事。不,oracle不知道这将如何影响现有的应用程序。@unreputable我理解你的观点。有许多变量需要考虑(特别是在这种情况下,更好地使用CPU的缓存),即使您经常使用子字符串,也可以平衡这一点。你可以深入了解更多信息。