WeakReferenceとhot deploy
java.lang.refパッケージで定義されているWeakReferenceが近頃のJ2EEコンテナなどで必要とされているhot deployを可能にするのに必要、との話が意外なところに書いてありました。Apache Commons LoggingのWeakHashtableのAPIドキュメント(http://commons.apache.org/logging/commons-logging-1.1/apidocs/org/apache/commons/logging/impl/WeakHashtable.html)です。調べてみれば、hot deployができるSeasarでも使われていましたし、NetBeansの中ではかなり使われていました。そもそも、この見慣れないWeakReferenceとは何者かと思い調べてみたので、忘れないようにメモです。
java.lang.refパッケージというのはJDK1.2から存在していたにもかかわらず、知る人ぞ知るのAPIだったようです。こちらのブログhttp://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.htmlは7年も前からあるのに、いかに知っている人が少ないかというエピソードで始まっていました。このパッケージはオブジェクトへの参照の強さを、通常のStrongにWeak, Soft, Phantomの3種類を加えて、GCが動いたときの動作を変えるために作られたようです。本来の設計の意図はメモリ管理のためで、memory leakをできるだけ減らすことだったようです。ただし、設計に問題があったらしく、JDK1.4より以前のバージョンではSoftReferenceが動いていなかったようです。その経緯についてはhttp://javaspecialists.eu/archive/Issue098.htmlにいろいろと書いてありました。java.lang.refパッケージそのものについては
- Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
- http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html
- Java 101: Trash talk, Part 2 | JavaWorld
- Javaのメモリ管理
に詳しい説明がありました。
さて、Soft, Weak Phantomの中では、よく使われているWeakReferenceのオブジェクトにするとどのように動くのかを、JavaWorldの記事を参考に、このようなサンプルプログラムを作って試してみました。
import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; class Employee { private String name; Employee(String name) { this.name = name; } @Override public String toString() { return name; } } public class WeakReferenceDemo { public static void main(String[] args) { Employee alice = new Employee("Alice"); Employee bob = new Employee("Bob"); ReferenceQueue queue = new ReferenceQueue(); WeakReference weakRef1 = new WeakReference(alice, queue); WeakReference weakRef2 = new WeakReference(bob, queue); alice = null; bob = null; System.gc(); Reference reference; while ( (reference = queue.poll()) != null) { if (reference == weakRef1) { System.out.println("Alice's WeakReference is cleared."); } else if (reference == weakRef2) { System.out.println("Bob's WeakReference is cleared."); } } } }
System.gc();の行にBreakpointを設定して眺めてみると、GC実行直前、直後でこのようにかわりました。
GCが動いてweak referenceしかなかったAliceとBobのEmployee型のオブジェクトは実体(weakRef1,weakRef2のreferent)が無くなっています。そして、ReferenceQueueに参照先が無くなったWeakReferenceが2つセットされました。queue.poll()メソッドを実行したときのメッセージ
Alice's WeakReference is cleared. Bob's WeakReference is cleared.
からも何がqueueに入っていたかを確認できます。ところが、
alice = null; //bob = null; System.gc(); Reference reference;
のようにstrong referenceを残しておくと、GCを実行した直後もweakRef2のreferentは残っているし、queueに入ったのもAliceの分ひとつだけです。
これは
alice = null; //bob = null; System.gc(); bob = null; System.gc(); Reference reference;
このようにBobのオブジェクトへのstrong referenceをなくして再びGCを実行すれば、weakRef2のreferentもnullになりqueueにweakRef2も追加されます。
WeakReferenceの機能を利用して、JVMを再起動しないでオブジェクトを入れ換えているという話がhttp://weblogs.java.net/blog/kirillcool/archive/2005/08/reflection_and.htmlのブログで紹介されていました。一度JVMで実行したクラスと同名のクラス、同名のメソッドの振舞を変えて、JVMを再起動せずに変更した方を実行するという内容です。この方法はhttp://xstream.codehaus.org/というライブラリで使われているようです。NetBeansではSoftReferenceを利用して
public final synchronized ClassLoader getClassLoader(boolean cache) { ClassLoader o = refClassLoader.get(); if (!cache || o == null) { o = ClassLoaderSupport.create(this); refClassLoader = new SoftReference(o); } return o; }
のようなことをしていたり、EventListenerのweak reference版であるWeakListenerやHashSetのweak版WeakHashSetを作っていたりしていました。また、
private static final Map> constructors = new WeakHashMap >();
のようなところもありました。
あまり知られていないとはいえ、java.lang.refパッケージは使われているところではかなり利用されているようですね。