Javaリファレンスタイプ

9215 ワード

JDK 1.2以降、javaは引用の概念を拡張し、引用を強引用、ソフト引用、弱引用、虚引用に分けた.
  • 強引用:コードの中に普遍的に存在するObject obj = new Object()のような引用を指し、強引用がまだ存在する限り、ゴミ収集器は参照されたオブジェクト
  • を永遠に回収しない.
  • ソフトリファレンス:いくつかはまだ役に立つが、重要なオブジェクトではない.ソフトリファレンスに関連付けられたオブジェクトについては、システムがメモリオーバーフローを起こす前に、これらのオブジェクトを回収範囲に入れて2回目の回収を行う.今回の回収に十分なメモリがなければ、メモリオーバーフロー異常を投げ出すことはできない.
  • 弱飲用:ゴミ収集器が作動すると、メモリが十分であるかどうかにかかわらず、飲用に関連する対象
  • のみが回収される.
  • 虚引用:1つのオブジェクトが虚引用の存在であるかどうかは、その生成時間に全く影響を及ぼさず、虚引用によって1つのオブジェクトインスタンスを取得することもできない.1つのオブジェクトに虚引用関連を設定する唯一の目的は、コレクタによって回収されたときに1つのシステム通知を受信することである.
    強参照
    JVMはGC時に強い参照のヒープインスタンスを解放しないので、ヒープ内GC後も十分なスペースが得られないとOOMが発生する
    String str = new String("Hi");
    

    以上の例では、スタックに割り当てるstrがスタックに割り当てるStringの例を指しているが、strの参照はこの例の強い参照である.
    配列と集合に保存する参照もMapに保存する参照も強い参照である.
    ソフトリファレンス
    JVMはGC時にソフトリファレンスで参照されたオブジェクトインスタンスを解放するとは限らないが、いつ解放されるのだろうか.JVMがヒープメモリ不足を発見した場合のみ、GC時にソフトリファレンスのヒープメモリを解放します.
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.SoftReference;
    
    public class TestSoft {
    
        public static void main(String[] args) throws InterruptedException {
            ReferenceQueue referenceQueue = new ReferenceQueue<>();
            User user = new User();
            SoftReference softReference = new SoftReference<>(user, referenceQueue);
            user = null;
    
            Thread t = new Thread(() -> {
                while (true) {
                    Reference extends User> ref = referenceQueue.poll();
                    if (ref != null) {
                        System.out.println("Changed : " + ref);
                        break;
                    }
                }
            });
    
            t.setDaemon(true);
            t.start();
    
            System.out.println("Before GC : " + " " + softReference.get());
    
            System.gc();
            System.out.println("After GC : " + softReference.get());
    
            byte[] array = new byte[1024 * 920 * 7];
            System.out.println("Alocate : " + softReference.get());
        }
    }
    
    class User {
        public String name;
    }
    

    仮想マシンパラメータ-Xmx10M -Xms10M -XX:PrintGCを指定し、このプログラムを実行した結果、次のようになります.
    [GC (Allocation Failure)  2048K->836K(9728K), 0.0023890 secs]
    Before GC :  testRef.User@404b9385
    [GC (System.gc())  1145K->844K(9728K), 0.0013400 secs]
    [Full GC (System.gc())  844K->750K(9728K), 0.0085260 secs]
    After GC : testRef.User@404b9385
    [GC (Allocation Failure)  788K->782K(9728K), 0.0003760 secs]
    [GC (Allocation Failure)  782K->782K(9728K), 0.0002590 secs]
    [Full GC (Allocation Failure)  782K->750K(9728K), 0.0043290 secs]
    [GC (Allocation Failure)  750K->750K(9728K), 0.0004580 secs]
    [Full GC (Allocation Failure)  750K->692K(9728K), 0.0079430 secs]
    Changed : java.lang.ref.SoftReference@19366529
    Alocate : null
    
    SoftReferenceインスタンスオブジェクトを構築する際、テストオブジェクトの追加に加えてReferenceQueueインスタンスオブジェクトを追加し、オブジェクトの到達可能な状態が変化するとSoftReferenceReferenceQueueキューに移動する.最後のPollという出力から、このオブジェクトは見えなくなった.
    弱い参照
    弱引用はソフトドリンクよりも弱い引用であり、JVMはGC時に弱引用を発見すれば、その引用したインスタンスを回収する
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.SoftReference;
    import java.lang.ref.WeakReference;
    
    public class TestSoft {
    
        public static void main(String[] args) throws InterruptedException {
            ReferenceQueue referenceQueue = new ReferenceQueue<>();
            User user = new User();
            WeakReference softReference = new WeakReference<>(user, referenceQueue);
            //        
            user = null;
    
            Thread t = new Thread(() -> {
                while (true) {
                    Reference extends User> ref = referenceQueue.poll();
                    if (ref != null) {
                        System.out.println("Changed : " + ref);
                        break;
                    }
                }
            });
    
            t.setDaemon(true);
            t.start();
    
            System.out.println("Before GC : " + " " + softReference.get());
    
            System.gc();
            System.out.println("After GC : " + softReference.get());
    
            byte[] array = new byte[1024 * 920 * 7];
            System.out.println("Alocate : " + softReference.get());
    
        }
    }
    
    class User {}
    

    仮想マシンパラメータ-Xmx10M -Xms10M -XX:+PrintGCを指定し、このプログラムを実行した結果、次のようになります.
    [GC (Allocation Failure)  2048K->800K(9728K), 0.0031060 secs]
    Before GC :  null
    Changed : java.lang.ref.WeakReference@175fdc70[GC (System.gc())  1084K->824K(9728K), 0.0011480 secs]
    [Full GC (System.gc())  824K->748K(9728K), 0.0088060 secs]
    
    After GC : null
    [GC (Allocation Failure)  807K->812K(9728K), 0.0010100 secs]
    [GC (Allocation Failure)  812K->844K(9728K), 0.0004150 secs]
    [Full GC (Allocation Failure)  844K->748K(9728K), 0.0090930 secs]
    [GC (Allocation Failure)  748K->748K(9728K), 0.0003230 secs]
    [Full GC (Allocation Failure)  748K->690K(9728K), 0.0082600 secs]
    Alocate : null
    
    WeakReferenceがオブジェクトインスタンスに保存されている場合はどうなりますか?
    import java.lang.ref.WeakReference;
    
    public class TestSoft {
        public static void main(String[] args) throws InterruptedException {
            WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
            weakReferenceCache.cache = new WeakReference<>(new User());
            System.out.println("Before GC : " + weakReferenceCache.cache.get());
            System.gc();
            System.out.println("After GC : " + weakReferenceCache.cache.get());
            byte[] array = new byte[1024 * 920 * 7];
            System.out.println("Alocate GC : " + weakReferenceCache.cache.get());
        }
    }
    
    class WeakReferenceCache {
        public WeakReference cache;
    
    }
    class User {
    }
    

    同じように実行して結果を見てみましょう
    Before GC : User@41629346
    After GC : null
    Alocate GC : null
    

    確かにGCごとに回収されています
    リストに保存すると
    import java.lang.ref.WeakReference;
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestSoft {
        public static void main(String[] args) throws InterruptedException {
            WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
            for (int i = 0; i < 10; i++) {
                WeakReference softReference = new WeakReference<>(new User());
                weakReferenceCache.cache.add(softReference);
            }
    
            System.out.println("Before GC : ");
            weakReferenceCache.cache.forEach(cache -> {
                System.out.println(cache.get());
            });
            System.gc();
            System.out.println("After GC : ");
            weakReferenceCache.cache.forEach(cache -> {
                System.out.println(cache.get());
            });
            byte[] array = new byte[1024 * 920 * 7];
            System.out.println("Alocate GC : ");
            weakReferenceCache.cache.forEach(cache -> {
                System.out.println(cache.get());
            });
        }
    }
    
    class WeakReferenceCache {
        public List> cache = new ArrayList<>();
    
    }
    class User {
    }
    

    結果は
    Before GC : 
    User@6433a2
    User@5910e440
    User@6267c3bb
    User@533ddba
    User@246b179d
    User@7a07c5b4
    User@26a1ab54
    User@3d646c37
    User@41cf53f9
    User@5a10411
    After GC : 
    null
    null
    null
    null
    null
    null
    null
    null
    null
    null
    Alocate GC : 
    null
    null
    null
    null
    null
    null
    null
    null
    null
    null
    

    配列に格納されていても同様に回収されます
    ダミーリファレンス
    虚引用はすべての引用タイプの中で最も弱いもので、虚引用で持たれているオブジェクトは持たれていない効果と基本的に同じです.虚引用からgetすると、空が得られます.それなら、なぜこのような引用を設計するのでしょうか.虚参照は参照キューと一緒にいなければならないため、いくつかのリソースのものを虚参照に配置して実行し、記録することができます.
    import java.lang.ref.*;
    
    public class TestSoft {
    
        public static void main(String[] args) throws InterruptedException {
            ReferenceQueue referenceQueue = new ReferenceQueue<>();
            User user = new User();
            PhantomReference softReference = new PhantomReference<>(user, referenceQueue);
            user = null;
    
            Thread t = new Thread(() -> {
                while (true) {
                    Reference extends User> ref = referenceQueue.poll();
                    if (ref != null) {
                        System.out.println("Changed : " + System.currentTimeMillis());
                        break;
                    }
                }
            });
    
            t.setDaemon(true);
            t.start();
    
            System.out.println("Before GC : " + System.currentTimeMillis() + " " + softReference.get());
    
            System.gc();
            System.out.println("After GC : " + softReference.get());
    
            byte[] array = new byte[1024 * 920 * 7];
            System.out.println("Alocate : " + softReference.get());
    
        }
    }
    
    class User {}
    

    仮想マシンパラメータ-Xmx30M -Xms30M -XX:+PrintGCを指定し、このプログラムを実行した結果、次のようになります.
    Before GC : 1462461362835 null
    [GC (System.gc())  2806K->904K(29696K), 0.0033390 secs]
    [Full GC (System.gc())  904K->779K(29696K), 0.0095950 secs]
    Changed : 1462461362850
    After GC : null
    Alocate : null