我正在搜索由java中的subString()方法引起的内存泄漏。我在互联网上阅读了一些文章,并获得了一些知识,但最终我有点困惑。一篇文章提到了“String类中的substring方法,调用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方法使用相同的字符数组来表示原始字符串,那么原始字符串如何保持不被垃圾回收?
同样我想知道内存泄漏是由于使用相同的字符数组来表示原始字符串引起的,因为新字符串{subString方法返回的字符串}与原始字符串或原始字符串相比会非常少字符串仍然没有被垃圾收集?
请有人帮我澄清一下
答案 0 :(得分:6)
当您在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[]
仍在正在使用的内存。只使用 part 的事实意味着可以回收其内存的 none 。
第二种方法较慢,因为它需要复制操作。但这确实意味着之前的String
可以完整清理,因为旧的char[]
已不再使用了。
Java 使用来遵循第一种方法,但是最近(Java 7以后)它已经切换到使用第二种方法。所以内存泄漏曾经是substring()
调用的问题,但不再是。
答案 1 :(得分:1)
措辞有点草率。原始的String实例将被垃圾收集。但它的内部字符数组赢了,因为它仍然被子字符串引用。
在极端情况下,您可以使用长度为1的String实例引用1GB数组。
在:
s1 ---------> object --> char[] (1GB large)
- offset
- count
调用substring:
s1 ---------> object --> char[] (1GB large)
- offset ^
- count |
|
substring --> object ---/
- offset
- count
s1超出范围:对象可以被垃圾收集
char[] (1GB large)
^
|
substring --> object ---/
- offset
- count
答案 2 :(得分:0)
根据@ chiastic-security的答案,也可以找到这个参考,以获得有关内存泄漏预防的Java 8处理的详细解释
http://mrgyani.com/java-interview/memory-leak-problem-in-java-substring/