Smile Engineering blog ( スマイルエンジニアリング・ブログ )

ジェイエスピーからTipsや技術特集、プロジェクト物語を発信します

WeakHashMapクラス

2012-06-11 11:51:04 | Java API
何年もの間Javaを触ってきていても全く使ったことがない機能というものがあったりする。
java.util.WeakHashMapクラスがその一つだ。
何となくは分かるのだが、これを使わなければならない場面に遭遇してこなかったからぼんやりとしたままだった。
それとも、ぼんやりとしたままだから、今こそ使うべき時、と判断できなかっただけなのか…。
最近になってWeakHashMapに関わらざるを得ない状況があった。
そこでこれを機会にWeakHashMapがどのように動くのか調べることにした。

最大の特徴は、その名前にもある通り"Weak"なMapであること。この"Weak"は<参照>が<弱い>ことを意味している。
Mapのキーを参照するモノがWeakHashMap自身の他に存在しなくなると、そのキーとキーに関連する値、つまりエントリがWeakHashMapから削除されるようにできている。
WeakHashMapからキーへの参照が<強い>java.util.HashMapではこのようなことは難しい。
WeakHashMapの中をちょっとだけ見てみる。
Mapはエントリ(java.util.Map.Entry)を単位として情報を保持するが、WeakHashMapの場合、エントリはjava.lang.ref.WeakReferenceクラスを継承する内部クラスjava.util.WeakHashMap.Entryによって表される(WeakHashMap.EntryクラスはMap.Entryインターフェイスをimplementsする)。
WeakHashMap.EntryがreferentとするのはWeakHashMapのキーである。これに加えて、WeakHashMapは中に専用のjava.lang.ref.ReferenceQueueを持つ。
これらが組み合わさることで"Weak"なHashMapが実現される。
キーとWeakHashMap.EntryとReferenceQueueはどうつながっているのか?
あるエントリ(キー=X, 値=Y)に着目して大雑把に流れを見ると、次のような4つのステップA~Dを経ることになるだろう。

A 結びつく
WeakHashMapにエントリ(X, Y)が追加される。…(1)

B 使われる
(色々なことが起こる…)

C 忘れられる
Xがどこからも参照されなくなる。…(2)

GCが実行される。…(3)

WeakHashMap内のReferenceQueueにエントリ(X, Y)が追加される。…(4)

D 消え去る
WeakHashMapのいずれかのpublicメソッドが呼ばれる。…(5)

XのエントリがWeakHashMapから削除される。…(6)

ステップA~Dは非同期に起こる出来事だが、出来事を時間順に並べるとおおよそA→B→C→Dの順になるだろう。
ステップBの間にもごく短い時間、どこからも参照されなくなることは何度となくある。
でもそこをGCに見つからなければ、ステップCには行かない。
WeakHashMapのpublicメソッドはいずれも、中でWeakHashMap#expungeStaleEntriesメソッドを呼ぶ。
これはReferenceQueueをチェックし、その時点で不要になったことが分かったエントリ(=stale entries...?)をすべてWeakHashMapから取り除く働きをする。
このstale entriesのうちの1つが「Xのエントリ」だ。まったくもって他力本願な仕組みである。
WeakHashMapを使っている連中がpublicメソッドを呼んだら、そのついでにゴミ掃除をさせているわけである。
ということはつまり、GCがReferenceQueueにエントリを追加するとすぐWeakHashMapから削除される、とはならないということか。
それどころか、いつまでも残っている可能性があるようだ。(まあ、それはレアケースだろう、多分。)
ただし勘違いしてはいけない。エントリがあいも変わらずWeakHashMapに残っているからといって、WeakHashMapからそのエントリを取り出す手段は、もはやない。
一旦ReferenceQueueに入れられたら(4)、後は消え去る(ステップD)のみである。
(3)の段階で「Xのエントリ」のキーはXでも、その他の何かでもなくなっている。
(3)でGCはWeakReferenceであるエントリからそのreferentであるキーを取り除いてしまっている。
キーのないエントリはただ存在するのみである。でもこれはちょっと気持ち悪い。要らなくなったらすぐにでも消えて欲しい。
そんな時にはスレッドが必要だ。
つまりWeakHashMap専用ReferenceQueue専用スレッド上でWeakHashMap専用ReferenceQueueを見張っていて、WeakHashMap専用ReferenceQueueにエントリが突っ込まれたらすぐにWeakHashMapから削除してやればよい…と言いたい所だが、そうは問屋が卸さない。
WeakHashMap専用ReferenceQueueはprivateなのだ。
だから、WeakHashMap専用ReferenceQueue専用スレッドを持たせようとすると、WeakHashMap自体を改造する必要が出てくる。
このような"ナントカMap"は自分で作るしかない。そして何よりも、まず先にスレッドという高価なモノを用意してまでやる価値があるかどうか考えた方がよいのだろう。
ちなみに、WeakHashMapはjava.util.AbstractHashMapを継承する。名前に"HashMap"とあるが直接java.util.HashMapクラスを継承している訳ではない。

株式会社ジェイエスピー
システム部
鈴木 誠司


monipet
  動物病院の犬猫の見守りをサポート
  病院を離れる夜間でも安心

ASSE/CORPA
  センサー、IoT、ビッグデータを活用して新たな価値を創造
  「できたらいいな」を「できる」に

OSGi対応 ECHONET Lite ミドルウェア
  短納期HEMS開発をサポート!

GuruPlug
  カードサイズ スマートサーバ

株式会社ジェイエスピー
  横浜に拠点を置くソフトウェア開発・システム開発・
  製品開発(monipet)、それに農業も手がけるIT企業