皆さんもご存じのとおり、Javaで唯一のインスタンスを使いまわすときは、次のプログラム例のようにSingletonデザインパターンを使います。
またこのプログラムでは、Singletonオブジェクトの生成を遅らせるためにstaticのインナークラスと組み合わせて使っています。「Lazy loading」などと呼ばれている手法です。
またこのプログラムでは、Singletonオブジェクトの生成を遅らせるためにstaticのインナークラスと組み合わせて使っています。「Lazy loading」などと呼ばれている手法です。
/** *Lazy loadingを用いたSingletonクラスです。 */ public class Singleton { private Singleton(){} /** *唯一のインスタンスを返します。 *@return 唯一のインスタンス */ public static Singleton getInstance() { return SingletonHolder.INSTANCE; } /** *getInstance()を最初に呼び出したときに、SingletonHolderクラスがロードされます。 *すると、INSTANCEフィールドにSingletonオブジェクトが代入されます。 */ private static class SingletonHolder { public static final Singleton INSTANCE = new Singleton(); } }
ところがSingletonデザインパターンには、Singletonのクラスを保持しているクラスローダが破棄されない限り、Singletonオブジェクトが生存し続ける問題点があります。そこで、java.lang.refパッケージのAPIと組み合わせる方法を提案します。次のプログラムを見てください。
/** *Lazy loadingとjava.lang.refパッケージを用いたSingletonクラスです。 */ public class Singleton { private Singleton() {} /** *唯一のインスタンスを返します。 *@return 唯一のインスタンス */ public static Singleton getInstance() { synchronized(SingletonHolder.class){ Singleton referent = SingletonHolder.INSTANCE.get(); if(referent == null){ referent = new Singleton(); SingletonHolder.INSTANCE = new WeakReference<>(referent); } return referent; } } /** *getInstance()を最初に呼び出したときに、SingletonHolderクラスがロードされます。 *すると、INSTANCEフィールドにSingletonオブジェクトが代入されます。 */ private static class SingletonHolder { public static Reference<singleton> INSTANCE = new WeakReference<>(new Singleton()); } }
WeakReferenceクラスを利用しています。外部プログラムがgetInstance()が返すSingletonオブジェクトを参照していなけなければ、マイナーGCが走るたびにSingletonインスタンスが破棄されるようになります。Singletonインスタンスの寿命が短くなり、Javaヒープの使用量を抑制できるでしょう。ところが、このようなプログラム例を記載した日本語記事が見当たりません。英語記事にはありますが、プログラム例がまちまちで怪しいプログラム例も散見されます。そこでどうしたら安全なSingletonになるかを考えてみたわけです。
WeakReference.get()はガーベジコレクション実行を挟んで、復帰値が変わる点に注意する必要があります。そこで上のプログラムの13行目では、WeakReference.get()の復帰値をreferent変数に代入させ、強参照を保持します。referentがnullだった場合、15行目で新しいリファレントをreferent変数に代入し、強参照を保持します。これによりgetInstance()を実行中にガーベジコレクタが起動しても、リファレントが破棄されるリスクを排除できるはずです。こうして18行目で有効なSingletonオブジェクトを確実に返せるでしょう。たとえば、15~16行目を1文にまとめて「SingletonHolder.INSTANCE = new WeakReference<>(new Singleton())」などとしていけないでしょう。
なお、Singletonインスタンスの寿命を延ばしたければ、16、27行目のWeakReferenceをSoftReferenceに変更すればよいです。SoftReferenceだとSingletonインスタンスがTenured世代領域までに昇進し、メモリ不足になるまで破棄されない点に注意します。
WeakReference.get()はガーベジコレクション実行を挟んで、復帰値が変わる点に注意する必要があります。そこで上のプログラムの13行目では、WeakReference.get()の復帰値をreferent変数に代入させ、強参照を保持します。referentがnullだった場合、15行目で新しいリファレントをreferent変数に代入し、強参照を保持します。これによりgetInstance()を実行中にガーベジコレクタが起動しても、リファレントが破棄されるリスクを排除できるはずです。こうして18行目で有効なSingletonオブジェクトを確実に返せるでしょう。たとえば、15~16行目を1文にまとめて「SingletonHolder.INSTANCE = new WeakReference<>(new Singleton())」などとしていけないでしょう。
なお、Singletonインスタンスの寿命を延ばしたければ、16、27行目のWeakReferenceをSoftReferenceに変更すればよいです。SoftReferenceだとSingletonインスタンスがTenured世代領域までに昇進し、メモリ不足になるまで破棄されない点に注意します。
0 件のコメント:
コメントを投稿