Javaには参照オブジェクトと呼ばれる、参照を抽象的に示すオブジェクトがあります。それが、java.lang.refパッケージのReferenceというクラスのオブジェクトです。このクラス自体は抽象クラスで、具象クラスには、
- WeakReference(弱参照)
- SoftReference(ソフト参照)
- PhantomReference(ファントム参照)
があります。
これらの参照オブジェクトによって参照されるインスタンスが、GCによってファイナライズされるときに、参照キューというものに参照オブジェクトが追加されます。その参照キューを示すのがReferenceQueueというクラスです。
次に参照の種類についてですが、
- 普通にフィールドなどで保持している場合は強参照
- SoftReferenceによって保持されている場合はソフト参照
- WeakReferenceによって保持されている場合は弱参照
- PhantomReferenceによって保持されている場合はファントム参照
という4種類の参照があります。
余談になりますが、そろぞれの参照に対してあるスレッドから到達できるかということを参照到達性といいます。参照到達可能な状態とは一般的に「参照されている」という状態を意味します。ちなみにJavaでは参照の管理を、参照カウンタではなく参照到達性という概念で管理しています。暗黙の参照や循環参照があっても、根元となるスレッドから参照できない場合はGCの対象となります。
ソフト参照と弱参照は実装によっては内部で同じ場合もありますが、一般的なJVMの実装ではソフト参照よりも弱参照の方がよりGCによってクリアされやすくなっています。ソフト参照や弱参照は、
- 強到達しない
- ソフト到達、弱到達する
- OutOfMemoryErrorになりそう
という場合にGCによって参照がクリアされる事が保障される参照です。
ファントム参照は特殊な参照で、通常の参照に似ていますが、インスタンスを格納すると二度と取得されません。ファントム参照自体が到達可能な状態であるとGCの対象にもなりません。その代わりファントム参照自体が到達不可能な状態になると、参照キューに入ります。結構使いどころが難しいですが、GCのタイミングをハンドリングする目的で使われる事が多いです。
弱参照が使われているケースとして、有名なのがjava.util.WeakHashMapというクラスです。このクラスはキーが弱参照で保持されます。簡単に言うと、キーが強到達しない場合、いつのまにか要素が消えるマップクラスです。逆に値がソフト参照されるクラスもあり、非公開APIでsun.misc.SoftCacheというクラスがあります。これは値が強到達しない場合、要素がなくなるマップクラスです。名前の通りキャッシュ目的で利用すると非常に有用です。
他に有用な利用目的として、クラスローダに対して横断的にクラスを保持したい場合です。例えばクラスのインスタンスを保持する必要がある場合に強参照してしまうと、クラスローダを切り捨てるときにクラスが強参照されているためクラスやクラスローダがGCの対象とならなくなります。WEBアプリでは必須です。
Tomcatでコンテキストのリロードを繰り返すうちにOutOfMemoryErrorになった経験はないでしょうか。これはスレッドとクラスローダとCommons Loggingによる強参照の関係によってメモリリークしているためです。
Commons Loggingは、クラスローダとクラスをキーにロガーのインスタンスをキャッシュしていて、クラスやクラスローダは強参照されます。(新しいCommons Loggingは弱参照しています)Tomcatは起動時にログを出力するためにCommons Loggingを使用している為、Commons Loggingはシステムクラスローダによってロードされます。そしてTomcatがコンテキストのリロードを行うときに、クラスローダを新規で作成し、スレッドに対するクラスローダの結びつきを変更します。このときに、結びつくスレッドがいなくなったクラスローダはGCの対象となり、同時にクラスもGCの対象になるはずなのですが、システムクラスローダによってロードされているCommons Loggingによって、クラスやクラスローダが強参照されているためGCの対象とならなくなり、OutOfMemoryErrorとなるわけです。
最後におまけですが、PhantomReferenceを使ったサンプルです。Queueに参照オブジェクトが追加されていく様子が分かると思います。
#仕組み自体が複雑なので、簡単に書いたつもりでもちょっと難しいかもしれません。質問があればコメント欄で。