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

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

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

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

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

2 回答 2

6

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


现在到你的具体例子。

重要提示:以下适用于较旧的 JVM;即 Java 7 更新 5 及更早版本。在 Java 7 更新 6 中,它们进行了更改String.substring(),以便目标字符串和生成的子字符串不共享支持数组。这消除了substring.


substring方法不会在新字符串中引用原始字符串。它实际上所做的是保存对原始字符串支持数组的引用;即保存字符的数组。

但是话虽如此,分配nullsamplel并不足以使整个原始字符串的状态不可达。原始字符串的整个后备数组将保持可访问性......这意味着它不会成为垃圾收集的候选者。

但还有另一个并发症。您设置sample1为 String 文字,并且表示 String 文字的 String 对象始终是可访问的(除非整个类被卸载!)

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

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

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


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

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

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

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


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

理论上会的。

在实践中,这取决于当 GC 最终开始查找时引用是否仍然可以访问......以及所讨论的字符串是否足够大并且足够多以对内存使用产生重大影响。

在实践中,通常不满足该前提条件,并且创建这样的新字符串通常无济于事。

于 2013-04-07T01:43:42.340 回答
2

请记住,在 JavaString中是不可变的。在这种情况下,sample1将被丢弃,但从sample2不指向sample1:它指向 JVM 中一个单独保存的不可变字符串,该字符串最迟在substring被调用时创建。

当您设置sample1为 null 时,它指向的内存可用于垃圾回收(假设没有其他字符串具有相同的值,并且没有其他变量指向该位置)。当您使用new关键字(或通过分配原语隐式这样做)时,会在堆上分配新内存(通常;同样,字符串是不可变的并共享相同的内存)。如果没有指针(读取:任何命名变量)指向给定的内存位置,则它可用于垃圾收集。

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

于 2013-04-07T01:41:18.457 回答