皆さんもご存じのとおり、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 件のコメント:
コメントを投稿