我正在维基百科中阅读WeakReference
,我看到了这段代码
public class ReferenceTest {
public static void main(String[] args) throws InterruptedException {
WeakReference r = new WeakReference(new String("I'm here"));
WeakReference sr = new WeakReference("I'm here");
System.out.println("before gc: r=" + r.get() + ", static=" + sr.get());
System.gc();
Thread.sleep(100);
// only r.get() becomes null
System.out.println("after gc: r=" + r.get() + ", static=" + sr.get());
}
}
当它运行时,这是结果
之前gc:r =我在这里,静态=我在这里
在gc:r = null之后,静态=我在这里
sr
和r
变量都是引用字符串对象。 r
现在已经被垃圾收集了,但为什么sr
在调用垃圾收集器后没有收集垃圾?
我只是好奇这是怎么发生的。
答案 0 :(得分:6)
不因为字符串池本身。
真正的原因是ReferenceTest
类具有对表示"I'm here"
文字的String对象的隐式硬引用。该硬引用意味着sr
中的弱引用不会被垃圾收集 1 打破。
事实上:
即使没有对应于文字的String对象,也需要隐式引用。 (他们 汇集...... JLS有效地需要这个...但我说即使它们不是,也需要引用。替代方案是Java每次评估一个字符串文字表达式时都要编写一个新的String对象。这将是可怕的效率低下!!)
字符串池内部使用弱引用形式...以便可以对未引用的实习字符串进行垃圾回收。如果不是这种情况,那么每个新的实习字符串都将是内存泄漏。
无论如何......如果你小心地构造一个字符串而不使用字符串文字并实习它,就像这样:
char[] chars = {'a', 'b', 'c'};
WeakReference r = new WeakReference(new String(chars).intern());
...你应该发现弱引用(最终)被破坏了。 (虽然可能需要几个GC周期。)
1 - 理论上,导致类被卸载和垃圾收集可以摆脱对该字符串文字的最后可到达的硬引用。但是,如果在这种情况下发生了这种情况,那么您将超过能够观察WeakReference对象状态的点。至少,使用您的示例代码。此外,除非使用自定义类加载器启动JVM,否则我认为不可能导致卸载入口类。这不是那种简单或有用的事情。
答案 1 :(得分:1)
这是因为字符串池。当你使用new运算符创建字符串时,它将在池中创建并可用于正常的垃圾收集。但是当你将字符串定义为文字时,它将在字符串池中创建,并且它不会用正常的gc操作收集垃圾
答案 2 :(得分:-1)
静态变量是Garbage仅在卸载类时收集。因此,您可以看到即使在手动触发垃圾回收后仍然会打印静态变量的值。