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パッケージそのものについては

に詳しい説明がありました。
さて、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実行直前、直後でこのようにかわりました。
http://www.servletgarden.com/images/weakreference_before_gc.png
http://www.servletgarden.com/images/weakreference_after_gc.png
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の分ひとつだけです。
http://www.servletgarden.com/images/weakreference_existing_strong_reference.png
これは

        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パッケージは使われているところではかなり利用されているようですね。