Javaにおける管理リソースのリファレンスキューに関する原理解析

6154 ワード

オブジェクトが達成可能な状態を変更すると、オブジェクトへの参照が参照キュー(referencequeue)に配置される可能性があります.これらのキューは、ゴミ回収器によって、私たちのコードコミュニケーションに関連するオブジェクトの達成性の変化に使用されます.これらのキューは、getメソッドの戻り値がnullであるかどうかを確認することによって、オブジェクトの達成性の変化を検出するのに最適な方法です.
リファレンスオブジェクトは、構築時に特定のキューに関連付けることができます.Referenceの各サブクラスには、次の形式のコンストラクタがあります.
.public Strength Reference(T reference,ReferenceQueueq):このメソッドは、指定された指称オブジェクトで新しい参照オブジェクトを作成し、指定されたキューに登録します.弱い参照とソフト参照は、ゴミ回収器が指称オブジェクトが人に入力された特定の達成可能な状態を決定した後、キューに挿入され、この2つの参照は人キューを挿入する前に消去されます.ダミーリファレンスは、ゴミ回収器が指称オブジェクトがダミー到達状態に入ったと判断した後、キューに挿入されますが、消去されません.リファレンスオブジェクトがゴミ回収器によってキューに挿入されると、getメソッドの戻り値はnullになるに違いないので、オブジェクトは二度と復活しません.
リファレンスオブジェクトをリファレンスキューに登録しても、キューとリファレンスオブジェクト間のリファレンスは作成されません.私たちの参照オブジェクト自体が到達不可能になった場合、キューを挿入することはできません.したがって、私たちのアプリケーションは、すべての参照オブジェクトに対する強い参照を維持する必要があります.
ReferenceQueueクラスでは、キューから参照を削除するための3つの方法が用意されています.
  •   .public Reference < ? extends下>poll():キュー内の次の参照オブジェクトを除去して返すために使用され、キューが空の場合null.
  • を返す.
  • .public Referencemove()throws InterruptedException:キュー内の次の参照オブジェクトを削除して戻す方法で、キューが使用可能な参照オブジェクトを返すまでブロックされます.
  • .public Referenceremove(long timeout)throws interrupte-dException:キュー内の次の参照オブジェクトを削除して戻すために使用されます.このメソッドは、キューが使用可能な参照オブジェクトを返す前にブロックされるか、指定したタイムアウトを超えて終了します.指定したタイムアウトを超えるとnullが返されます.指定したタイムアウトが0の場合、無期限に待機することを意味します.

  • pollメソッドにより、スレッドは、参照がキューにあるかどうかをクエリーし、その参照がキューに存在する場合に特定の動作を実行できます.removeメソッドは、キューから参照を除去し、適切な動作を実行する専門のスレッドがあるより複雑な(より珍しい)状況を処理することができる.これらの方法のブロック挙動はobject.waitで定義されたブロック挙動と同じである.特定の参照については、isEnqueuedメソッドを使用してキューに存在するかどうかを問い合せるか、enqueueメソッドを呼び出してキューに強制的に挿入することができますが、通常、このプラグインはゴミ回収器によって行われます.
    リファレンスキュー内のダミーリファレンスは、オブジェクトがいつ回収されるかを決定するために使用できます.仮想参照のgetメソッドは常にnullを返すため、他の方法でオブジェクトにアクセスすることはできません.実際には、弱い参照とソフト参照がオブジェクトが終了した後にキューに挿入されるため、仮想参照で回収するオブジェクトを検索するのが最も安全な方法です.虚参照は、オブジェクトが終了した後にキューに挿入されることを意味します.すなわち、オブジェクトが何らかの操作を実行できる最後の時点で挿入されるので、絶対に安全です.可能であれば、他のリファレンスがfinalizeメソッドで終端可能オブジェクトを使用する可能性があるため、常に虚リファレンスを使用する必要があります.
    外部リソースセットへのアクセスを制御できるリソースマネージャの例を考慮します.オブジェクトは、外部リソースへのアクセスを要求し、操作が完了するまでアクセスを終了することができます.その後、使用したリソースをリソースマネージャに返す必要があります.このリソースが共有されている場合、その使用権は複数のオブジェクト間で伝達され、複数のスレッド間で伝達される可能性があります.そのため、どのオブジェクトがこのリソースの最後のユーザーであるかを決定することが難しく、どのコードがこのリソースに戻るのかを決定することも難しいです.このような状況を処理するために、リソースマネージャは、キー(key)と呼ばれる特殊なオブジェクトにリソースを関連付けることによって、このリソースの自動回収を実現することができる.キーオブジェクトが到達できる限り、このリソースはまだ使用されていると考えられます.キーオブジェクトがゴミ回収として扱われる限り、このリソースは自動的に解放されます.次のコードは、上記のリソースの抽象的な表現です.
    
      interface Resource{
    
      void use(Object key, Object…args);
    
      void release();
    
      }
    
    

    リソースを取得する場合は、キーオブジェクトをリソースマネージャに提供する必要があります.このリソースは、返却されたResourceインスタンスに対して、対応するキーが得られた場合にのみ使用できます.これにより、キーが回収された後も、このリソースを表すResourceオブジェクト自体が利用可能であっても、対応するリソースが使用されなくなることを保証できます.Resourceオブジェクトには、キーオブジェクトへの強い参照が格納されていないことに注意してください.これは、キーオブジェクトが到達不可能になり、リソースが回収されないことを防止するために重要です.Resourceのインプリメンテーションは、リソースマネージャにネストできます.
    
      private static class ResourceImpl implements Resource{
    
      int keyHash;
    
      boolean needsRelease=false
    
      ResourceImpl(Object key){
    
      keyHash=System.identityHashCode(key);
    
      //=set up the external resource
    
      needsRelease=true; }
    
      public void use(Object key,Object... args){
    
      if (System.identityHashCode(key)!=keyHash)
    
      throw new IlleqalArgumentException("wrong key"
    
      //...use the resource
    
      }
    
      public synchronized void release(){
    
      if (needsRelease){
    
      needsRelease=false:
    
      //=release the resource
    
      }
    
      }
    
      }
    
    

    リソースが作成されると、キーオブジェクトのハッシュコードが格納され、useメソッドが呼び出されるたびに、同じキーが提供されているかどうかを確認します.リソースの実際の使用には同期が必要かもしれませんが、簡単にするためにここでは省略します.releaseメソッドは、リソースの使用者が使用終了後に直接呼び出すか、キーオブジェクトが参照されなくなったときにリソースマネージャによって呼び出すことができるリソースを解放します.独立したスレッドを使用して参照キューを監視するため、releaseメソッドはsynchronizedであり、複数回の呼び出しを許可する必要があります.
    実際のエクスプローラには、次の形式があります.
    
      public final class ResourceManager{
    
      final ReferenceQueue
    
    

                                 キー・オブジェクトは任意のオブジェクトであってもよく、リソース・マネージャにキー・オブジェクトを割り当てるよりも、リソース・コンシューマに大きな柔軟性を与えます.getResourceメソッドを呼び出すと、新しいResourceワークmplオブジェクトが作成され、そのメソッドに提供されるキーがこの新しいResourceImplオブジェクトに渡されます.次に、オブジェクトがメソッドに渡されるキーであることを示す虚参照が作成され、その後、リソースマネージャの参照キューに挿入されます.最後に作成されたダミー参照と参照オブジェクトは、マッピングテーブルに格納されます.このマッピングテーブルには、すべてのダミー参照オブジェクトを達成できるようにする2つの用途があります.2つ目は、各ダミー参照に関連付けられた実際のResourceオブジェクトをクエリーする便利な方法を提供することです.(もう1つの方法は、PhantomReferenceをサブクラス化し、Resourceオブジェクトをフィールドに存在させることです.)
    キーオブジェクトが到達不可能になった場合、リソースマネージャは独立したreaperスレッドを使用してリソースを処理します.shutdownメソッドは、割り込みに応答するために刈り取りスレッドを終了することによってgetResourceメソッドがIlle-llleStateException例外を放出することによってリソースマネージャを「閉じる」.この簡単な設計では、リソースマネージャが閉じた後にプラグインキューの参照は処理されません.実際の刈り取り機スレッドは次のとおりです.
    
      class ReaperThread extends Thread{
    
      public void run(){
    
      //run until interrupted
    
      while (true){
    
      try{
    
      Reference ref=queue.remove();
    
      Resource res=null;
    
      synchronized(ResourceManager.this){
    
      res=refs.get(ref);
    
      refs . remove(ref);
    
      }
    
      res .release();
    
      ref.clear();
    
      }
    
      catch (InterruptedException ex){
    
      break;//all done
    
      }
    
      }
    
      }
    
      }
    
    

    ReaperThreadは内部クラスであり、それに関連付けられたエクスプローラが閉じるまで、指定された刈り取りスレッドが実行されます.このスレッドは、特定のキーに関連付けられた虚参照が参照キューに挿入されるまでremoveメソッドでブロックされます.このダミーリファレンスは、マッピングテーブルからResourceオブジェクトへのリファレンスを取得し、このキーリファレンスペアはマッピングテーブルから削除されます.次に、リソースを解放するためにResourceオブジェクト上でreleaseメソッドを呼び出します.最後に、
    虚参照がクリアされ、キーが回収されるようになります.
    独立したスレッドを使用する代わりに、参照キュー上でpollメソッドを呼び出し、そのキーが到達不可能になったすべてのリソースを解放する操作はgetResourcec「メソッド」で置き換えることができ、shutdowメソッドは最後のpoll操作を実行するためにも使用することができる.リソースマネージャの意味は、実際のリソースタイプとリソース使用のモードに依存します.
    リファレンスキューを使用する設計は、エンドポイント(特にダミーリファレンス)を直接使用する設計に比べて、より信頼性が高い.しかし、参照オブジェクトが参照キューに挿入された正確な時間と場所は特定できません.アプリケーションが終了したときに、すべての挿入可能な参照が参照キューにナイフで挿入されたかどうかも判断できません.すべてのリソースがアプリケーションが終了する前に解放されることを確認する必要がある場合は、必要なフックを閉じるか、アプリケーションによって定義された他のプロトコルを使用して実現する必要があります.