本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

Java 2参照クラスを使用するためのガイドライン

SoftReference、WeakReference、PhantomReferenceの効果的な使用法を学ぶ

Peter Haggar (haggar@us.ibm.com), Senior Software Engineer, IBM
Peter Haggarは、アメリカのノースカロライナ州にあるResearch Triangle Parkに勤務するIBMシニア・ソフトウェア・エンジニアであり、 「 Practical Java Programming Language Guide」 (Addison-Wesley 社発行) の著者です。彼はまた、Javaプログラミングに関する多数の記事を発表しています。彼は、開発ツール、クラス・ライブラリー、およびオペレーティング・システムなど、幅広いプログラミング経験を持っています。IBMでは、未来のインターネット・テクノロジーに携わり、現在は高性能なWebサービスを中心に研究を行っています。Peterは、多くの業界の会議で頻繁に技術的なスピーチを行っています。彼はIBMに14年以上にわたって勤務しており、また、Clarkson Universityよりコンピューター・サイエンスのB.S. を取得しています。Peterの連絡先はhaggar@us.ibm.com です。

概要: Java 2プラットフォームでは、java.lang.ref パッケージが導入され、この中にはオブジェクトをメモリーに固定することなく参照を可能にするクラスが含まれています。これらのクラスはまた、限定された範囲内でガーベッジ・コレクターとのやりとりを提供します。今回の記事では、Peter Haggar氏が、SoftReferenceWeakReference、およびPhantomReference クラスの機能と振る舞いについて説明し、これらを使用するためのお勧めのプログラミング・イディオムを紹介します。

日付:  2002年 10月 01日
レベル:  初級 この記事の原文:  英語
アクティビティー: 2559 ビュー
お気軽にご意見・ご感想をお寄せください: 


java.lang.ref パッケージ (SoftReferenceWeakReference、およびPhantomReference クラスが含まれる) がJava 2プラットフォームの一部として初めて導入されたとき、その有用性について過剰宣伝されたといえるでしょう。パッケージに含まれるクラスは有効かもしれませんが、一定の制限があるために、これらのクラスの魅力が乏しくなり、その適用を限定された問題に狭めました。

ガーベッジ・コレクションの概要

参照クラスの主な機能は、ガーベッジ・コレクターによってまだ再利用できる状態にあるオブジェクトを参照できることです。参照クラスが導入されるまでは、強参照しか利用できませんでした。たとえば、以下のコード行は、obj という強参照を示しています。

Object obj = new Object();

obj 参照は、ヒープに保存されているオブジェクトを参照します。obj 参照が存在する限り、ガーベッジ・コレクターは、オブジェクトを保持するために使用されているストレージを解放しません。

obj が有効なスコープから外れたり、明示的にnull に割り当てられると、オブジェクトは、それへの参照が他にないとすると、回収できるようになります。ただし、ここで特に注意すべき点は、オブジェクトが回収できる状態になっているというだけで、ガーベッジ・コレクターが動作して再利用できる状態になっているわけではないということです。ガーベッジ・コレクション・アルゴリズムにはさまざまなものがあり、より古く、長期間使用されたオブジェクトについては短期のオブジェクトほど頻繁に分析しないアルゴリズムもあります。したがって、回収できる状態にあるオブジェクトは、全く再利用されない可能性があります。このことは、ガーベッジ・コレクターがオブジェクトを解放する前にプログラムが終了するような場合に起こる可能性があります。要するに、回収可能なオブジェクトがガーベッジ・コレクターによって必ず回収されるという保証は全くないということです。

この情報は、参照クラスを分析する際に重要になります。ガーベッジ・コレクションの性質上、これらのクラスは当初考えていたほど有効ではないかもしれませんが、特定の問題に対しては有効なクラスといえます。ソフト参照オブジェクト、弱参照オブジェクト、ファントム参照オブジェクトは、それぞれ異なる方法で、回収を妨げることなくヒープ・オブジェクトを参照します。各参照オブジェクトにはそれぞれの振る舞いがあり、ガーベッジ・コレクターとの相互作用も異なります。また、新しい参照クラスはすべて、典型的な強参照よりも「弱い」タイプの参照を表します。さらに、メモリー内のオブジェクトは、強、ソフト、弱、ファントムの複数の参照によって参照することができます。先に進む前に、いくつかの用語について説明します。

  • 強可到達: 強参照によってアクセスできるオブジェクト。
  • ソフト可到達: 強可到達ではなく、ソフト参照によってアクセスできるオブジェクト。
  • 弱可到達: 強可到達またはソフト可到達ではなく、弱参照によってアクセスできるオブジェクト。
  • ファントム可到達: 強可到達、ソフト可到達、または弱可到達ではなく、ファントム参照によってアクセスできる、ファイナライズされたオブジェクト。
  • クリア: 参照オブジェクトの対象フィールド (referent field) をnull に設定し、参照クラスが参照したヒープ内のオブジェクトをfinalizable として宣言すること。

SoftReferenceクラス

SoftReference クラスの典型的な使用例は、メモリー・センシティブ・キャッシュです。SoftReference の概念は、JVMがメモリー不足状態を報告する前に、ソフト参照のすべてがクリアされることを保証された状態で、オブジェクトの参照を維持することです。重要な点は、ガーベッジ・コレクターの実行中は、ソフト可到達オブジェクトを解放する可能性も解放しない可能性もあるということです。オブジェクトが解放されるかどうかは、ガーベッジ・コレクターのアルゴリズムと、コレクターの実行中に使用可能なメモリーの量によって決まります。

WeakReferenceクラス

WeakReference クラスの典型的な使用例は、canonicalized mappingです。また、弱参照は、この仕組みがなければ長期にわたって存在し、再作成のコストもそれほどかからないオブジェクトに使うと役立ちます。重要な点は、ガーベッジ・コレクターの実行中に、弱可到達オブジェクトが発生した場合、WeakReference が参照するオブジェクトをガーベッジ・コレクターが解放することです。ただし、弱可到達オブジェクトを発見して解放する前に、ガーベッジ・コレクターが複数回実行される可能性があることには注意してください。

PhantomReferenceクラス

PhantomReference クラスは、参照しているオブジェクトを回収する直前を監視したい場合にのみ役立ちます。つまり、回収直前のクリーンアップ操作の実行に使用できます。PhantomReference は、ReferenceQueue クラスとともに使用しなければなりません。ReferenceQueue は、通知のメカニズムとして機能するので必要なものです。オブジェクトがファントム可到達であるとガーベッジ・コレクターが判断すると、PhantomReference オブジェクトがそのReferenceQueue に置かれます。PhantomReference オブジェクトをReferenceQueue に置かれたということは、PhantomReference オブジェクトが参照したオブジェクトがファイナライズされており、いつでも回収できる状態にあるということを示す通知となります。これにより皆さんは、オブジェクト・メモリーが再利用される直前に措置を講ずることができます。


ガーベッジ・コレクターと参照の相互作用

ガーベッジ・コレクターは、実行のたびに、もはや強可到達ではなくなったオブジェクト・メモリーを随意に解放します。ガーベッジ・コレクターが、ソフト可到達オブジェクトを発見すると、以下のようになります。

  • SoftReference オブジェクトの対象フィールドがnull に設定され、heap オブジェクトを参照しなくなる。
  • SoftReference によって参照されていたheap オブジェクトがfinalizable と宣言される。
  • heap オブジェクトのfinalize() メソッドが実行され、そのメモリーが解放されると、SoftReference オブジェクトが、(存在する場合は)ReferenceQueue に追加される。

ガーベッジ・コレクターが、弱可到達オブジェクトを発見すると、以下のようになります。

  • WeakReference オブジェクトの対象フィールドがnull に設定され、heap オブジェクトを参照しなくなる。
  • WeakReference によって参照されていたheap オブジェクトがfinalizable と宣言される。
  • heap オブジェクトのfinalize() メソッドが実行され、そのメモリーが解放されると、WeakReference オブジェクトが、(存在する場合は)ReferenceQueue に追加される。

ガーベッジ・コレクターが、ファントム可到達オブジェクトを発見すると、以下のようになります。

  • PhantomReference によって参照されているheap オブジェクトがfinalizable と宣言される。
  • ソフト参照や弱参照と異なり、PhantomReference は、ヒープ・オブジェクトが解放される前にReferenceQueue に追加されます (すべてのPhantomReference オブジェクトは、関連するReferenceQueueと一緒に作成しなければならないことを思い出してください)。これにより、ヒープ・オブジェクトが再利用される前に措置を講ずることができます。

リスト1のコードについて考えてみましょう。図1は、そのコードの実行を示しています。



リスト1. WeakReferenceとReferenceQueueを使用するコード例
                
//Create a strong reference to an object
MyObject obj = new MyObject();                  //1
//Create a reference queue
ReferenceQueue rq = new ReferenceQueue();       //2
 
//Create a weakReference to obj and associate our reference queue
WeakReference wr = new WeakReference(obj, rq);  //3


図1. リスト1のコードの行 //1、//2、//3の実行後のオブジェクト・レイアウト
図1

図1は、各コード行の実行後のオブジェクトの状態を示しています。行 //1でオブジェクトMyObject を作成し、行 //2でReferenceQueue オブジェクトを作成します。行 //3で、対象のMyObjectReferenceQueueを参照するWeakReference オブジェクトを作成します。objrqwr の各オブジェクト参照はすべて、強参照であることに注意してください。これらの参照クラスが働くようにするには、objnull に設定して、MyObject オブジェクトの強参照を解除しなければなりません。これを行わない限りMyObject オブジェクトは再利用されず、参照クラスの利点がまったくなくなることを思い出してください。

各参照クラスにはget() メソッドがあり、ReferenceQueue クラスにはpoll() メソッドがあります。get() メソッドは、対象オブジェクトへの参照を戻します。PhantomReferenceget() を呼び出すと、必ずnull を戻します。これは、PhantomReference が、コレクションの追跡にのみ使用されるからです。poll() メソッドは、キューに追加された参照オブジェクトを戻し、キューに何もない場合はnull を戻します。したがって、リスト1の実行後にget() メソッドとpoll() メソッドを呼び出した結果は、以下のようになります。

wr.get();   //returns reference to MyObject
rq.poll();  //returns null
			

ここで、ガーベッジ・コレクターが動作するとします。MyObject オブジェクトが解放されていないので、get() メソッドとpoll() メソッドは同じ値を戻します。obj は、依然としてそれに対する強参照を維持します。実際には、オブジェクト・レイアウトは変わらず、図1のように見えます。しかし、以下のコードを考えてみましょう。

obj = null;
System.gc();  //run the collector
			

このコードを実行すると、オブジェクト・レイアウトは図2に示すようになります。


図2. obj = null後のオブジェクト・レイアウト (ガーベッジ・コレクター実行中)
図2

ここでは、get() メソッドとpoll() メソッドの呼び出し結果が変わります。

wr.get();   //returns null
rq.poll();  //returns a reference to the WeakReference object
			

この状態が示しているのは、初めはWeakReference オブジェクトによって保持されていたMyObject オブジェクトがもはや取り出せなくなったことです。つまり、ガーベッジ・コレクターがMyObject 用のメモリーを解放し、WeakReference オブジェクトがそのReferenceQueue に配置可能になったということです。したがって、WeakReference またはSoftReference クラスのget() メソッドがnull を戻すと、オブジェクトがfinalizable と宣言され、必ずというわけではありませんが、回収された可能性があるということがわかります。ファイナライズが完了し、heap オブジェクトのメモリーが回収された場合にのみ、WeakReference またはSoftReference が、関連するReferenceQueue に置かれます。リスト2は、こうした原理の一部を示す完全に機能するプログラムを示しています。多くのコメントとprintステートメントがあるので、コードは比較的簡単に理解できます。


リスト2. 参照クラスの構造を示す完全なプログラム
                
import java.lang.ref.*;
class MyObject
{
  protected void finalize() throws Throwable
  {
    System.out.println("In finalize method for this object: " + this);
  }
}
class ReferenceUsage
{
  public static void main(String args[])
  {
    hold();
    release();
  }
  public static void hold()
  {
    System.out.println("Example of incorrectly holding a strong " +
                       "reference");
    //Create an object
    MyObject obj = new MyObject();
    System.out.println("object is " + obj);
    //Create a reference queue
    ReferenceQueue rq = new ReferenceQueue();
    //Create a weakReference to obj and associate our reference queue
    WeakReference wr = new WeakReference(obj, rq);
    System.out.println("The weak reference is " + wr);
    //Check to see if it's on the ref queue yet
    System.out.println("Polling the reference queue returns " + rq.poll());
    System.out.println("Getting the referent from the " +
                       "weak reference returns " + wr.get());
    System.out.println("Calling GC");
    System.gc();
    System.out.println("Polling the reference queue returns " + rq.poll());
    System.out.println("Getting the referent from the " +
                       "weak reference returns " + wr.get());
  }
  public static void release()
  {
    System.out.println("");
    System.out.println("Example of correctly releasing a strong " +
                       "reference");
    //Create an object
    MyObject obj = new MyObject();
    System.out.println("object is " + obj);
    //Create a reference queue
    ReferenceQueue rq = new ReferenceQueue();
    //Create a weakReference to obj and associate our reference queue
    WeakReference wr = new WeakReference(obj, rq);
    System.out.println("The weak reference is " + wr);
    //Check to see if it's on the ref queue yet
    System.out.println("Polling the reference queue returns " + rq.poll());
    System.out.println("Getting the referent from the " +
                       "weak reference returns " + wr.get());
    System.out.println("Set the obj reference to null and call GC");
    obj = null;
    System.gc();
    System.out.println("Polling the reference queue returns " + rq.poll());
    System.out.println("Getting the referent from the " +
                       "weak reference returns " + wr.get());
  }
}


使用法とイディオム

こうしたクラスの背後にある考えは、アプリケーションが存続する間メモリー内にオブジェクトを固定するのを回避することです。固定するのではなく、オブジェクトへの参照をソフトに、あるいは弱く、またはファントムに行い、ガーベッジ・コレクターが随意にオブジェクトを解放できるようにします。このような使用法は、アプリケーションがその存続期間中使用するヒープ・メモリーの量を最小限に抑えたい場合に有効となります。これらのクラスを利用する場合は、オブジェクトへの強参照を使用することはできないということを覚えておかなければなりません。強参照を使用すると、これらのクラスがもたらす利点をすべて失うことになります。

また、オブジェクトを使用する前に、正しいプログラミング・イディオムを使用して、コレクターがそれを再利用しているかどうかをチェックし、再利用している場合には、まずオブジェクトを再作成しなければなりません。このプロセスは、さまざまなプログラミング・イディオムによって行うことができます。誤ったイディオムを選択すると、問題が発生する可能性があります。リスト3の、WeakReference から対象オブジェクトを検索するためのコード・イディオムを見てみましょう。


リスト3. 対象オブジェクトを検索するためのイディオム
                
obj = wr.get();
if (obj == null)
{
  wr = new WeakReference(recreateIt());  //1
  obj = wr.get();                        //2
}
//code that works with obj

このコードを理解した後で、リスト4の、WeakReference から対象オブジェクトを検索するための代替コード・イディオムを見てみましょう。


リスト4. 対象オブジェクトを検索するための代替イディオム
                
obj = wr.get();
if (obj == null)
{
  obj = recreateIt();                    //1
  wr = new WeakReference(obj);           //2
}
//code that works with obj

これら2つのイディオムを比較して、どちらが適切に機能し、どちらが機能しないかを判断できるかどうか考えてみましょう。リスト3に示すイディオムは必ずしも適切に機能しませんが、リスト4のイディオムは機能します。リスト3のイディオムが不完全である理由は、if ブロックの本文が完成した後に、obj が nullでないことが保証されていないということです。リスト3で、行 //1の後、行 //2が実行される前に、ガーベッジ・コレクターが実行されるとどうなるかについて考えてみましょう。recreateIt() メソッドがオブジェクトを再作成しますが、強参照ではなくWeakReference によって参照されます。つまり、再作成されたオブジェクトに行 //2が強参照を割り当てる前にコレクターが実行されると、オブジェクトが失われ、wr.get()null を戻します。

リスト4では、行 //1がオブジェクトを再作成し、それに強参照を割り当てるので、そのような問題は起こりません。したがって、ガーベッジ・コレクターがこの行の後で、しかも行 //2の前に実行されても、オブジェクトは再利用されないということになります。続いて、obj に対するWeakReference が行 //2で作成されます。このif ブロックの後にobj を処理してから、objnull に設定すると、ガーベッジ・コレクターがこのオブジェクトを再利用できるようになり、弱参照機能を完全に利用できます。リスト5は、今まで説明したイディオムの違いを示す完結したプログラムです (このプログラムを実行するには、プログラムが実行されるディレクトリーに「temp.fil」ファイルが必要です)。


リスト5. 正しいプログラミング・イディオムと誤ったプログラミング・イディオムを示す完結したプログラム
                
import java.io.*;
import java.lang.ref.*;
class ReferenceIdiom
{
  public static void main(String args[]) throws FileNotFoundException
  {
    broken();
    correct();
  }
  public static FileReader recreateIt() throws FileNotFoundException
  {
    return new FileReader("temp.fil");
  }
  public static void broken() throws FileNotFoundException
  {
    System.out.println("Executing method broken");
    FileReader obj = recreateIt();
    WeakReference wr = new WeakReference(obj);
    System.out.println("wr refers to object " + wr.get());
    System.out.println("Now, clear the reference and run GC");
    //Clear the strong reference, then run GC to collect obj.
    obj = null;
    System.gc();
    System.out.println("wr refers to object " + wr.get());
    //Now see if obj was collected and recreate it if it was.
    obj = (FileReader)wr.get();
    if (obj == null)
    {
      System.out.println("Now, recreate the object and wrap it in a WeakReference");
      wr = new WeakReference(recreateIt());
      System.gc();  //FileReader object is NOT pinned...there is no
      //strong reference to it.  Therefore, the next //line can return null.
      obj = (FileReader)wr.get();
    }
    System.out.println("wr refers to object " + wr.get());
  }
  public static void correct() throws FileNotFoundException
  {
    System.out.println("");
    System.out.println("Executing method correct");
    FileReader obj = recreateIt();
    WeakReference wr = new WeakReference(obj);
    System.out.println("wr refers to object " + wr.get());
    System.out.println("Now, clear the reference and run GC");
    //Clear the strong reference, then run GC to collect obj
    obj = null;
    System.gc();
    System.out.println("wr refers to object " + wr.get());
    //Now see if obj was collected and recreate it if it was.
    obj = (FileReader)wr.get();
    if (obj == null)
    {
      System.out.println("Now, recreate the object and wrap it in a WeakReference");
      obj = recreateIt();
      System.gc();  //FileReader is pinned, this will not affect //anything.
      wr = new WeakReference(obj);
    }
    System.out.println("wr refers to object " + wr.get());
  }
}


まとめ

参照クラスは、適切な状態で使用した場合に有効なものとなります。しかし、その有効性は、ガーベッジ・コレクターの予測できない振る舞いに依存しているという事実によって弱められます。また、それらの効果的な使用は正しいプログラミング・イディオムの適用にも依存しているため、そうしたクラスがどのように実装されているのか、またそれらに対してどのようにプログラミングするのかを理解することが不可欠です。


参考文献

  • Sam Borman氏が、IBM Garbage Collectorについて興味深い連載記事を執筆しています。第1回 ではオブジェクトの割り振りについて、第2回ではガーベッジ・コレクションの詳細について 説明しています。

  • Jeff Friesen氏は、JavaWorld の記事 (2002年1月) で、Reference Object APIを使用して、イメージ・キャッシュを管理する方法、有効オブジェクト (significant object) が強可到達でなくなったときに通知を取得する方法、およびファイナライズ後のクリーンアップを実行する方法を示しています。

  • developerWorksJava technologyゾーンでJavaプログラミングに関する数多くの参考文献をご覧ください。

著者について

Peter Haggarは、アメリカのノースカロライナ州にあるResearch Triangle Parkに勤務するIBMシニア・ソフトウェア・エンジニアであり、 「 Practical Java Programming Language Guide」 (Addison-Wesley 社発行) の著者です。彼はまた、Javaプログラミングに関する多数の記事を発表しています。彼は、開発ツール、クラス・ライブラリー、およびオペレーティング・システムなど、幅広いプログラミング経験を持っています。IBMでは、未来のインターネット・テクノロジーに携わり、現在は高性能なWebサービスを中心に研究を行っています。Peterは、多くの業界の会議で頻繁に技術的なスピーチを行っています。彼はIBMに14年以上にわたって勤務しており、また、Clarkson Universityよりコンピューター・サイエンスのB.S. を取得しています。Peterの連絡先はhaggar@us.ibm.com です。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=229416
ArticleTitle=Java 2参照クラスを使用するためのガイドライン
publish-date=10012002
author1-email=haggar@us.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。