4

我正在阅读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, static=I'm here

srr变量都是引用字符串对象。r现在是垃圾收集但是,为什么sr在调用垃圾收集器后没有垃圾收集?

我只是好奇这是怎么发生的。

4

3 回答 3

8

不是因为字符串池本身

真正的原因是ReferenceTest该类具有对表示"I'm here"字面量的 String 对象的隐式硬引用。该硬引用意味着弱引用 insr不会被垃圾收集1破坏。

实际上:

  • 隐式引用是必要的,即使对应于文字的 String 对象没有被池化。(它们池化的...... JLS 实际上需要这个......但我是说即使它们不是引用也会需要。另一种方法是让 Java 每次创建并“实习”一个新的 String 对象字符串文字表达式被评估。那将是非常低效的!!)

  • 字符串池在内部使用了一种弱引用形式……以便可以对未引用的内部字符串进行垃圾收集。如果不是这种情况,那么调用String.intern()可能是无法治愈的内存泄漏。

无论如何......如果你小心地构造一个字符串而不使用字符串文字并实习它,就像这样:

    char[] chars = {'a', 'b', 'c'};
    WeakReference r = new WeakReference(new String(chars).intern());

...您应该发现弱引用(最终)被破坏了。(不过可能需要几个 GC 周期。)


1 - 理论上,导致类被卸载和垃圾收集可以摆脱对该字符串文字的最后一个可访问的硬引用。但是,如果在这种情况下发生这种情况,您将无法观察到 WeakReference 对象的状态。至少,在您的示例代码中。此外,除非您使用自定义类加载器启动 JVM,否则我认为不可能导致入口点类的卸载。这不是一件容易或有用的事情。

于 2013-01-24T06:11:50.107 回答
1

这是因为字符串池。当您使用 new 运算符创建字符串时,它将在池中创建并可用于正常的垃圾收集。但是当您将字符串定义为文字时,它将在字符串池中创建,并且不会被垃圾收集正常 gc 操作

于 2013-01-24T06:04:41.487 回答
-1

静态变量仅在卸载类时才会被垃圾收集。因此,您可以看到即使在您手动触发垃圾回收之后,静态变量的值仍然会被打印出来。

于 2013-01-24T06:01:12.923 回答