Java子串内存泄漏

时间:2013-11-19 20:46:28

标签: java memory-leaks garbage-collection

基于有关获取String Java String.split memory leak?的子字符串的讨论,我一直在分析两个示例子字符串的使用示例。

据说,如果调用者在对象中存储字段的子字符串,则不会收集对象的垃圾。 当我运行代码时,我得到OutofMemory Exception,并在通过VisualVM监视它时看到char []分配大小的增加

public class TestGC {
    private String largeString = new String(new byte[100000]);    
    String getString() {
        return this.largeString.substring(0,2);     
        //return new String(this.largeString.substring(0,2));
    }

    public static void main(String[] args) {
        java.util.ArrayList<String> list = new java.util.ArrayList<String>();
        for (int i = 0; i < 100000; i++) {
            TestGC gc = new TestGC();            
            list.add(gc.getString());            
        }
    }
}

使用以下代码,我没有收到错误,在通过VisualVM分析内存使用情况后,我意识到分配的char []大小越来越大,然后在某个时候以某种方式减少,然后再次增加并在某些时候减少(GC运行它的工作)。它永远持续下去。

public class TestGC {
    private String largeString = new String(new byte[100000]);

    String getString() {
        //return this.largeString.substring(0,2);       
        return new String(this.largeString.substring(0,2));
    }

    public static void main(String[] args) {
        java.util.ArrayList<String> list = new java.util.ArrayList<String>();
        for (int i = 0; i < 100000; i++) {
            TestGC gc = new TestGC();            
            list.add(gc.getString());            
        }
    }
}

我真的想了解GC收集的内容然后在第二个示例中从堆内存中删除了吗? 为什么GC不能在第一个例子中收集相同的对象?

在第一个示例largeString.substring(0,2));发送引用,在第二个示例new String(this.largeString.substring(0,2));创建新对象。两种情况都不应该对GC的行为有问题吗?

5 个答案:

答案 0 :(得分:3)

在第一个示例中,每次围绕循环创建新的TestGC对象时,您还要创建一个从100000字节数组初始化的新String。当您调用String.substring时,您返回相同的大长字符串,但偏移量设置为0并且count设置为2.因此所有数据仍然在内存中但是当您使用String时,您将只看到指定的2个字符子串调用。

在第二个示例中,每次循环时都会再次创建新的String,但是通过调用new String(String.substring),您将丢弃String的其余部分,并且只保留2个字符在内存中,所以其余的可以垃圾收集。

正如评论中的链接所说,这种行为在1.7.0_06中发生了变化,因此String.substring返回的String将不再共享相同的char []。

答案 1 :(得分:2)

我不希望你在Java 7中描述的行为,因为子串现在处理方式完全不同。但是......

在Java 6中

在第一个示例中,您在列表中存储的子字符串使用与TestGC对象内的原始String相同的字符数组,因此该字符数组无法返回到堆中。

在第二个示例中,在执行复制时,会为新String分配其自己的字符数组,因此当TestGC超出范围时,原始String 可以返回到堆。因此,在循环的每次迭代中都不会泄漏100000个字节。

答案 2 :(得分:0)

new String()显式构造函数调用使用char[]的相关部分的副本创建一个新的String实例(与底层的char[]的第一个示例相对。共享{1}}。因此,在第二个示例中,巨大的String在每个循环中分配,但在循环结束时丢弃TestGC实例后丢弃。

答案 3 :(得分:0)

我对大卫华莱士和戴夫约翰斯顿的所有答案和评论都有所了解。

以下是对象表示中的第一个示例引用 enter image description here

以下是对象表示中的第二个示例引用 enter image description here

答案 4 :(得分:0)

解决JDK 1.6中的内存泄漏问题

http://javaexplorer03.blogspot.in/2015/10/how-to-resolve-memory-leak-in-jdk-16.html

subString = string.substring(3,10)+“”;

在上面的代码中,string.substring(3,10)将返回指向原始字符串数组的子字符串,子字符串将不允许旧字符串的垃圾收集(char value [])。

但是当我们将空字符串添加到offset时,新字符串将在具有新char值[]数组的常量池中形成,我们可以克服旧字符串数组的垃圾收集问题。