【javaベース】Javaでの強参照、ソフト参照、弱参照、虚参照

7958 ワード

JDK 1.2バージョンから、オブジェクトの参照を4つのレベルに分け、プログラムがオブジェクトのライフサイクルをより柔軟に制御できるようにします.この4つのレベルは、強参照、ソフト参照、弱参照、虚参照の順に高くなります.
一、強引用オブジェクトに強い引用がある場合は、必ず少なくない生活用品に似ており、ゴミ回収器は決して回収しません.メモリ容量が不足している場合、Java仮想マシンはOutOfMemoryErrorエラーを投げ出してプログラムを異常に終了させ、強い参照を持つオブジェクトを勝手に回収してメモリ不足の問題を解決することはありません.
例:  
Object o=new Object();       
Object o1=o;     

 上のコードの第1文はheapスタックの中で新しいObjectオブジェクトを作成してoを通じてこのオブジェクトを引用して、第2文はoを通じてo 1からnew Object()というheapスタックの中のオブジェクトの引用を創立して、この2つの引用はすべて強い引用です.heapの中のオブジェクトに対する引用が存在する限り、gcはそのオブジェクトを収集しません.
o=null;       
o1=null;   

oおよびo 1がnullであるか、範囲外であるかを明示的に設定すると、gcはオブジェクトに参照が存在しないと判断し、収集することができる.収集可能はすぐに収集されるのと同じではなく、いつ収集されるかはgcのアルゴリズムに依存し、多くの不確実性をもたらす.たとえば、次のgc実行時に収集したいオブジェクトを指定したい場合は、仕方がありません.他の3つの参照があればできます.他の3つの参照はgc収集を妨げない場合に簡単なインタラクションを行うことができる.
Heapのオブジェクトには,強可及オブジェクト,軟可及オブジェクト,弱可及オブジェクト,虚可及オブジェクト,および到達不可オブジェクトがある.適用される強弱の順序は、強、軟、弱、および虚である.対象がどのような対象に属するかは、彼の最強の引用によって決まる.次のようになります.
    String abc=new String("abc");  //1       
    SoftReference<String> abcSoftRef=new SoftReference<String>(abc);  //2       
    WeakReference<String> abcWeakRef = new WeakReference<String>(abc); //3       
    abc=null; //4       
    abcSoftRef.clear();//5   

  上のコードでは、
    最初の行はheapペアに「abc」という内容のオブジェクトを作成し、abcからそのオブジェクトへの強い参照を確立します.このオブジェクトは強いです.
    2行目と3行目は、heap内のオブジェクトに対するソフトリファレンスと弱いリファレンスをそれぞれ確立します.この場合、heap内のオブジェクトは依然として強力です.
    4行目以降heapではオブジェクトが強く及ばず、ソフトになります.同様に5行目が実行されると弱くなります.
 
二、ソフトリファレンス(SoftReference)
1つのオブジェクトがソフトリファレンスのみを持っている場合は、物のある生活用品に似ています.メモリ容量が十分であれば、ゴミ回収器は回収されず、メモリ容量が不足している場合は、これらのオブジェクトのメモリが回収されます.ゴミ回収器が回収していない限り、そのオブジェクトはプログラムで使用できます.ソフトリファレンスは、メモリに敏感なキャッシュを実現するために使用できます.ソフトリファレンスはリファレンスキュー(ReferenceQueue)と組み合わせて使用できます.ソフトリファレンスが参照するオブジェクトがゴミ回収されると、Java仮想マシンはこのソフトリファレンスを関連するリファレンスキューに追加します.
ソフトリファレンスは、主にメモリに敏感なキャッシュに使用されます.jvmがメモリ不足を報告する前に、すべてのソフトリファレンスがクリアされます.これにより、gcはソフトの及ぶオブジェクトを収集し、メモリの緊迫した問題を解決し、メモリのオーバーフローを回避することができます.いつ収集されるかは、gcのアルゴリズムとgcの実行時に使用可能なメモリのサイズに依存します.gcがソフトリファレンスを収集することを決定した場合、abcSoftRefを例に挙げると、以下の手順が実行されます.
    1 abcSoftRefのreferenceをnullに設定し、heapのnew String(「abc」)オブジェクトを参照しません.
    2 heapのnew String(「abc」)オブジェクトを終了可能に設定します.
    3 heap内のnew String(「abc」)オブジェクトのfinalize()メソッドが実行され、そのオブジェクトが占有するメモリが解放されると、abcSoftRefがそのReferenceQueueに追加される.
   注意:ReferenceQueueへのソフトリファレンスと弱いリファレンスはありますか?   
Reference(T paramT, ReferenceQueue<? super T>paramReferenceQueue)    

Soft Referenceで指定されたオブジェクトは、Direct Referenceがなくても消去されません.SOftReferenceは、JVMメモリが不足し、Direct Referenceがない場合に消去されます.object-cacheを設計するために使用されます.これにより、SoftReferenceはオブジェクトをcacheするだけでなく、メモリ不足のエラーも発生しません(OutOfMemoryError).Soft Referenceも実作poolingのテクニックに適していると思います. 
     A obj = new A();    
    Refenrence sr = new SoftReference(obj);    
      
    //       
    if(sr!=null){    
        obj = sr.get();    
    }else{    
        obj = new A();    
        sr = new SoftReference(obj);    
    }   

三、弱引用(WeakReference)オブジェクトが弱引用のみを持っている場合、可物の生活用品に似ています.弱いリファレンスとソフトリファレンスの違いは、弱いリファレンスのみを持つオブジェクトがより短いライフサイクルを持つことです.ごみ捨てスレッドが管理するメモリ領域をスキャンする過程で、現在のメモリ領域が十分であるかどうかにかかわらず、弱い参照のみを持つオブジェクトが発見されると、メモリが回収されます.ただし、ゴミ回収器は優先度の低いスレッドであるため、弱い参照のみを持つオブジェクトをすぐに発見するとは限らない.弱いリファレンスはリファレンスキュー(ReferenceQueue)と連携して使用できます.弱いリファレンスがリファレンスしたオブジェクトがゴミ回収されると、Java仮想マシンはこの弱いリファレンスを関連付けられたリファレンスキューに追加します.
gcが弱いオブジェクトに遭遇し、abcWeakRefの参照を解放してオブジェクトを収集する.しかし、gcは、この弱い対象を見つけるために、この運用を必要とする可能性がある.次のコードによって、その役割が明らかになります. 
    String abc=new String("abc");       
    WeakReference<String> abcWeakRef = new WeakReference<String>(abc);       
    abc=null;       
    System.out.println("before gc: "+abcWeakRef.get());       
    System.gc();       
    System.out.println("after gc: "+abcWeakRef.get());     

実行結果:   
before gc: abc    
after gc: null   
gc収集弱可及オブジェクトの実行過程はソフト可及と同様であるが、gcはメモリの状況に応じてそのオブジェクトを収集するかどうかを決定しない.
オブジェクトの情報をいつでも取得したいが、そのオブジェクトのゴミ収集に影響を与えたくない場合は、一般的なreferenceではなくWeak Referenceでオブジェクトを覚えておく必要があります.
    A obj = new A();    
      
        WeakReference wr = new WeakReference(obj);    
      
        obj = null;    
      
        //      ,obj            
      ...    
      
      if (wr.get()==null) {    
      System.out.println("obj        ");    
      } else {    
      System.out.println("obj      ,     "+obj.toString());   
      }   
      ...   
    }  

 この例では、get()を介してこのReferenceの指すオブジェクトを取得することができ、戻り値がnullであれば、このオブジェクトがクリアされたことを表す. 
このようなテクニックは、OptimizerやDebuggerのようなプログラムを設計する際によく使われます.このようなプログラムは、あるオブジェクトの情報を取得する必要がありますが、このオブジェクトのゴミ収集に影響を与えることはできません.
 
四、虚引用(PhantomReference)「虚引用」はその名の通り、形は虚構であり、他のいくつかの引用とは異なり、虚引用は対象のライフサイクルを決定しない.オブジェクトがダミー参照のみを持っている場合は、参照がない場合と同様に、いつでもゴミに回収される可能性があります.ダミーリファレンスは、主にオブジェクトがゴミ回収されたアクティビティを追跡するために使用されます.
ダミーリファレンスとソフトリファレンスと弱リファレンスの違いは、ダミーリファレンスがリファレンスキュー(ReferenceQueue)と組み合わせて使用される必要があることです.ゴミ回収器がオブジェクトを回収する準備をしている場合、ダミーリファレンスがあることが判明すると、オブジェクトのメモリを回収する前に、このダミーリファレンスを関連するリファレンスキューに追加します.プログラムは、参照キューに虚参照が含まれているかどうかを判断することによって、参照されたオブジェクトがゴミ回収されるかどうかを知ることができる.プログラムは、仮想リファレンスがリファレンスキューに追加されていることを発見した場合、リファレンスされたオブジェクトのメモリが回収される前に必要な行動をとることができます. 
虚参照を確立した後、getメソッドで結果を返すのは常にnullであり、ソースコードを通じて、虚参照は参照するオブジェクトをreferentに書くことに通じ、getメソッドがnullを返すだけであることがわかります.まずgcと対話する過程を見て、彼の役割を話してみましょう.
  1 referentをnullに設定ことなく、heap中のnewString(「abc」)オブジェクトをそのまま終了可能(finalizable)に設定.
  2ソフトリファレンスや弱リファレンスとは異なり、まずPhantomReferenceオブジェクトをそのReferenceQueueに追加する.その後、虚の及ぶオブジェクトを解放する. 
   heapのnew String(「abc」)オブジェクトを収集する前に、他のことをすることができます.以下のコードで彼の役割を知ることができます.
    import java.lang.ref.PhantomReference;       
    import java.lang.ref.Reference;       
    import java.lang.ref.ReferenceQueue;       
    import java.lang.reflect.Field;       
          
    public class Test {       
        public static boolean isRun = true;       
          
        public static void main(String[] args) throws Exception {       
            String abc = new String("abc");       
            System.out.println(abc.getClass() + "@" + abc.hashCode());       
            final ReferenceQueue referenceQueue = new ReferenceQueue<String>();       
            new Thread() {       
                public void run() {       
                    while (isRun) {       
                        Object o = referenceQueue.poll();       
                        if (o != null) {       
                            try {       
                                Field rereferent = Reference.class      
                                        .getDeclaredField("referent");       
                                rereferent.setAccessible(true);       
                                Object result = rereferent.get(o);       
                                System.out.println("gc will collect:"      
                                        + result.getClass() + "@"      
                                        + result.hashCode());       
                            } catch (Exception e) {       
          
                                e.printStackTrace();       
                            }       
                        }       
                    }       
                }       
            }.start();       
            PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,       
                    referenceQueue);       
            abc = null;       
            Thread.currentThread().sleep(3000);       
            System.gc();       
            Thread.currentThread().sleep(3000);       
            isRun = false;       
        }       
          
    }