JDKコアJAVAソースコード解析(3)-参照関連


このシリーズを書きたくて長い間、自分に対しても総括と向上です.もともとJAVAを学ぶ时、あれらのJAVAの入门の本はあなたにいくつかの规则と法则を教えて、しかし使う时私达は普通とても思い出しにくくて、私达が使うのが少なくてしかもどうして分からないためです.それを知ってこそ、印象に残って勉強して役に立つ.
この文章は引用分析に対して,後で各種の枠組みメカニズムを分析する基礎である.
Javaリファレンス関連
強参照(Strong Reference)
強参照とは、プログラムコードの中に普遍的に存在する、一般的なnewオブジェクトがオブジェクト変数に値を付与することであり、強参照である.あるオブジェクトに強い参照が関連付けられている限り、JVMは必ずこのオブジェクトを回収しません.メモリが不足している場合でも、JVMはOutOfMemoryエラーを投げ出してもこのオブジェクトを回収しません.
Object object = new Object();
String str = "hello";

強い参照とオブジェクトとの関連付けを中断する場合は、参照をnullとして表示的に割り当てることができます.これにより、JVMは適切な時間にオブジェクトを回収します.多くのJAVAコレクションクラスは、ArrayListのようなオブジェクトを削除します.
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

ソフトリファレンス(Soft reference)
ソフトリファレンスは、Javaでjavaを使用する、役に立つが必要ではないオブジェクトを記述するために使用される.lang.ref.SoftReferenceクラスで表されます.ソフトリファレンスに関連付けられたオブジェクトについては、メモリオーバーフロー異常が発生する前に、これらのオブジェクトを回収範囲に列挙して2回目の回収を行います.今回の回収で十分なメモリがない場合、メモリオーバーフロー異常が放出されます.ソフトリファレンスが関連付けられているオブジェクトの場合、メモリが十分であれば、ゴミ回収器はそのオブジェクトを回収しません.メモリが足りない場合は、これらのオブジェクトのメモリを回収します.したがって、この点はOOMの問題を解決するのによく用いられ、この特性は、ウェブキャッシュ、ピクチャキャッシュなどのキャッシュを実現するのに適している.ソフトリファレンスはリファレンスキュー(ReferenceQueue)と組み合わせて使用できます.ソフトリファレンスが参照するオブジェクトがJVMによって回収されると、このソフトリファレンスはそれに関連付けられたリファレンスキューに追加されます.
ソフトリファレンスの初期化とテスト
public class SoftReferenceDemo {
    public static void main(String[] args) {
        //   ,     
        TestNormalObject testNormalObject = new TestNormalObject();
        //     
        SoftReference testNormalObjectSoftReference = new SoftReference<>(testNormalObject);
        //   ,     
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        //     
        SoftReference testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject);

        //     。         
        testNormalObject = null;
        testFinalizeObject = null;

        //      
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        try {
            //          -Xmx16m,                
            //           
            byte[] test = new byte[1024 * 1024 * 16];
        } catch (Error e) {
            System.out.println(e.getClass().getName() + ":" + e.getMessage());
        }

        //      
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());
    }

    /**
     *     。   finalize
     */
    public static class TestNormalObject {
    }

    /**
     *     。  finalize  
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

出力:
SoftReferenceDemo$TestNormalObject@31b7dea0
SoftReferenceDemo$TestFinalizeObject@3ac42916
Finalize is called
java.lang.OutOfMemoryError:Java heap space
null
null

ソフトリファレンスがゴミ回収されているかどうかを見る1つの方法はget()メソッドでnull判定が返されているかどうかを見るか、または構造時にキューが渡されているかどうかを見て、その後、このキューにこのオブジェクトがあるかどうかによって回収されているかどうかを判断することです.
public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue testNormalObjectReferenceQueue = new ReferenceQueue<>();
        ReferenceQueue testFinalizeObjectReferenceQueue = new ReferenceQueue<>();

        //   ,     
        TestNormalObject testNormalObject = new TestNormalObject();
        //     
        SoftReference testNormalObjectSoftReference = new SoftReference<>(testNormalObject, testNormalObjectReferenceQueue);
        //   ,     
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        //     
        SoftReference testFinalizeObjectSoftReference = new SoftReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);

        System.out.println("Origin: " + testNormalObjectSoftReference);
        System.out.println("Origin: " + testFinalizeObjectSoftReference);


        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread1.start();
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread2.start();

        //     。         
        testNormalObject = null;
        testFinalizeObject = null;

        //      
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        try {
            //          -Xmx16m,                
            //           
            byte[] test = new byte[1024 * 1024 * 16];
        } catch (Error e) {
            System.out.println(e.getClass().getName() + ":" + e.getMessage());
        }

        //      
        System.out.println(testNormalObjectSoftReference.get());
        System.out.println(testFinalizeObjectSoftReference.get());

        thread1.join();
        thread2.join();
    }

    /**
     *     。   finalize
     */
    public static class TestNormalObject {
    }

    /**
     *     。  finalize  
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

この場合、出力は確定しません.
Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
GOT From Queue: null
GOT From Queue: null
Finalize is called

次の場合があります.
Origin: java.lang.ref.SoftReference@31b7dea0
Origin: java.lang.ref.SoftReference@3ac42916
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestNormalObject@22a71081
com.github.hashZhang.scanfold.jdk.reference.SoftReferenceDemo$TestFinalizeObject@3930015a
java.lang.OutOfMemoryError:Java heap space
null
null
Finalize is called
GOT From Queue: null
GOT From Queue: null
Finalize is calledにはGOT From Queue: nullの前後順序が不確定であり、他にも様々な結果があり、このような原因はマルチスレッドのほか、ソフトリファレンスについてはGCが発生し、システムがメモリオーバーフロー異常を発生する前に、このオブジェクトがソフトリファレンスのみであればキューに入れられるためである.キューに入れてから、本当に回収されます.
弱引用WeakReference
必須でないオブジェクトを記述するために使用されますが、ソフトリファレンスよりも強度が弱く、弱いリファレンスに関連付けられたオブジェクトは次のゴミ収集が発生する前にしか生存できません.ゴミ収集器が動作すると、現在のメモリが十分であるかどうかにかかわらず、弱い参照のみで関連付けられたオブジェクトが回収されます.Javaではjavaを使用します.lang.ref.WeakReferenceクラスで表す
弱いリファレンスの初期化とテスト
public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue testNormalObjectReferenceQueue = new ReferenceQueue<>();
        ReferenceQueue testFinalizeObjectReferenceQueue = new ReferenceQueue<>();

        TestNormalObject testNormalObject = new TestNormalObject();
        WeakReference testNormalObjectWeakReference = new WeakReference<>(testNormalObject, testNormalObjectReferenceQueue);
        TestFinalizeObject testFinalizeObject = new TestFinalizeObject();
        WeakReference testFinalizeObjectWeakReference = new WeakReference<>(testFinalizeObject, testFinalizeObjectReferenceQueue);

        System.out.println(testNormalObjectWeakReference);
        System.out.println(testFinalizeObjectWeakReference);

        //     。         
        testNormalObject = null;
        testFinalizeObject = null;

        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testNormalObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread1.start();
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("GOT From Queue: " + testFinalizeObjectReferenceQueue.remove().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread2.start();

        //    GC,        GC
        System.gc();

        thread1.join();
        thread2.join();
    }

    /**
     *     。   finalize
     */
    public static class TestNormalObject {
    }

    /**
     *     。  finalize  
     */
    public static class TestFinalizeObject {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize is called");
        }
    }
}

出力は次のようになります.
java.lang.ref.WeakReference@6d8acf
java.lang.ref.WeakReference@182830e
Finalize is called
GOT From Queue: null
GOT From Queue: null

弱いリファレンスの場合、このオブジェクトが弱いリファレンスのみである限り、キューに入れられます.キューに入れてから本当に回収され、GCさえあれば回収されます
虚参照Phantom Reference
ダミーリファレンスは特殊で、1つのオブジェクトが回収されたかどうかを記録するためにのみ使用されます.GCプロセスは複雑であり,JavaプログラマーはGC可能なオブジェクトが回収されるかどうか分からない.この場合、虚引用が必要です.
ダミーリファレンスは、前のソフトリファレンス、弱いリファレンスとは異なり、オブジェクトのライフサイクルに影響しません.Javaでjavaを使用します.lang.ref.PhantomReferenceクラス表現.オブジェクトがダミーリファレンスに関連付けられている場合、リファレンスが関連付けられていない場合と同様に、ゴミ回収器によっていつでも回収される可能性があります.
虚参照と弱参照はソフト参照とは異なり、虚参照は、オブジェクトのfinalizeメソッドが呼び出された後(クラスがこのfinalizeメソッドを上書きしている場合)のみ、回収された後、初期化時のReferenceQueueに入る.他の2つの参照は、ReferenceQueueに先に入った後にオブジェクトのfinalizeメソッドが呼び出された後に回収される.
思考finalize()
まずfinalize()を実現するとGCにどのような影響があるかを見てみましょう.Mark-Sweep方式のGCにとって、unreachableのオブジェクトがfinalizableの場合、Minor GCはすぐに殺すことができないことを知っています.finalize()メソッドを先に実行する必要があります.しかしfinalize()メソッドMinor GC自体は実行できません.Finalizerのfinalizer daemon threadスレッドが実行を担当する必要があります.だからMinor GCは仕方なくfinalization queueに挿入するしかありません.後でいつfinalizer daemon threadが引き継がれるかを待って、キュー内のオブジェクトのfinalize()メソッドを1つずつ実行します.そのため、この対象を回収するには2ラウンドGCが必要です.また、finalize()の実行時間が特に長く、オブジェクトの生成速度よりも遅い場合は、メモリオーバーフローのリスクも発生します.だからとにかく、finalize()!!!
まとめ
  • 強参照:通常宣言のオブジェクト付与変数は強参照であり、OOMであっても
  • は回収されない.
  • ソフトリファレンス:OOMをトリガーすると
  • が回収されます.
  • 弱引用:GCが発生すれば回収される
  • ダミーリファレンス:オブジェクトが回収されたかどうかをマークするために使用されるリファレンスです.初期化は必ずキューに渡さなければなりません.そうしないと意味がありません.ダミーリファレンスと弱リファレンスはソフトリファレンスとは異なり、ダミーリファレンスは、オブジェクトのfinalizeメソッドが呼び出された後(クラスがこのfinalizeメソッドを上書きしている場合)のみ、回収された後、初期化時のReferenceQueueに入る.他の2つのリファレンスは、先にReferenceQueueに入った後にオブジェクトのfinalizeメソッドが呼び出された後に回収される.
  • finalize():finalize()!!!