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=私はここにいます, static=私はここにいます

gc の後: r=null、static=私はここにいます

srr変数はどちらも文字列オブジェクトを参照しています。はガベージ コレクションになりましたが、ガベージ コレクタを呼び出した後にガベージ コレクションが行われなかっrたのはなぜですか?sr

これがどのように起こったのか、私はただ興味があります。

4

3 に答える 3

8

文字列プーリング自体が原因ではありません

本当の理由は、クラスがリテラルReferenceTestを表すStringオブジェクトへの暗黙のハード参照を持っていることです。"I'm here"その強参照は、の弱参照がガベージコレクション1srによって壊されないことを意味します。

実際には:

  • リテラルに対応する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 に答える