基于有关获取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的行为有问题吗?
答案 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)
我对大卫华莱士和戴夫约翰斯顿的所有答案和评论都有所了解。
以下是对象表示中的第一个示例引用
以下是对象表示中的第二个示例引用
答案 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值[]数组的常量池中形成,我们可以克服旧字符串数组的垃圾收集问题。