另一个字符串的子字符串是否会阻止父字符串被垃圾回收?

时间:2013-04-07 01:23:42

标签: java garbage-collection

String samplel = "ToBeGarbageCollected";
String sample2 = samplel.substring(0, 1);
samplel = null;

我知道内部的子字符串将保留原始字符串的引用。

但是通过明确地将samplel定义为null,sample1和sample2是否可用于垃圾收集?

我记得在某处,如果父对象显式设置为null,则所有子值都可用于垃圾回收。这对上述情况有利吗? 我只是好奇这是否是父母子女关系的情景?如果不是,这会导致sample1sample2可用于垃圾收集吗?

String samplel = "ToBeGarbageCollected";
String sample2 = new String(samplel .substring(0, 1));
samplel  = null;

2 个答案:

答案 0 :(得分:6)

首先要说的是垃圾收集不会立即发生。因此,将null分配给任何内容都不会/不能导致垃圾回收。 可能做的是使对象变得无法访问 ...这将使其成为未来GC运行中垃圾收集的潜在候选者。


现在举个具体的例子。

重要说明:以下适用于较旧的JVM;即Java 7更新5及更早版本。在Java 7更新6中,他们更改了String.substring(),以便目标字符串和生成的子字符串不共享后备数组。这消除了substring潜在的存储泄漏问题。


substring方法不会在新String中引用原始String。它实际上做的是保存对原始String的后备数组的引用;即包含字符的数组。

但话说回来,将null分配给samplel并不足以使整个原始字符串的状态无法访问。原始String的整个后备阵列将保持可访问状态......这意味着它不会成为垃圾收集的候选者。

但还有另一个复杂因素。您将sample1设置为String文字,并且表示字符串文字的String对象始终可以访问(除非整个类被卸载!)

  

但是通过将samplel显式定义为null,sample1和sample2是否可用于垃圾收集?

原始sample1对象将保持完全可访问状态,并且sample2将保持可访问状态,除非该变量超出范围。

如果sample1不是文字,并且没有其他参考文献,那么答案就会有所不同。 sample1对象无法访问,但其后备数组仍可通过sample2访问。


在第二个示例中,复制子字符串会导致创建新的String。并且保证新String不会与原始String和临时子字符串共享后备数组。在这种情况下,分配null是不必要的。

  

现在,sample1和sample2都可用于垃圾收集吗?

对于sample1是文字的情况,答案与上述答案相同。

如果sample1不是文字,并且没有其他引用,那么sample1和临时子字符串现在将无法访问。


  

我只想知道String构造函数在哪里有用。

理论上它会是。

在实践中,它取决于GC最终是否仍然可以查看引用...以及相关的字符串是否足够大且数量足以对内存使用产生重大影响。

在实践中,前提条件通常不满足并且创建一个新的字符串,就像通常一样没有帮助。

答案 1 :(得分:2)

请记住,在Java中String是不可变的。在这种情况下,sample1将被丢弃,但sample2从未指向sample1:它指向JVM中单独持有的不可变字符串,该字符串最迟在{{1}时创建被叫了。

当您将substring设置为null时,它指向的内存可用于垃圾收集(假设没有其他字符串保持相同的值,并且没有其他变量指向该位置)。当您使用sample1关键字(或通过赋予基元隐式地执行此操作)时,会在堆上分配新内存(通常;字符串是不可变的并且共享相同的内存)。如果没有指针(读取:任何命名变量)指向给定的内存位置,它可用于垃圾收集。

请记住:在没有对象引用的任何情况下,它都可用于垃圾回收。对象不是由分配给它们的变量名定义的,而是内存中的位置,变量名称充当这些对象的指针(引用)。字符串有些不同,因为它们是不可变的,并且JVM可能会选择不进行垃圾收集,原因与引用无关。