そういえば、大昔に「よく分からん」とか言って放り投げていた java.lang.ref パッケージ関連のクラスとその周辺知識。
先日、soft reference とかの話が出てきたので、今度はちゃんと読んでみた。(とりあえず到達可能性について)
java.lang.ref (Java 2 Platform SE 5.0)
到達可能性については以下のような感じ。
- 強到達可能 なオブジェクト:
- 一番説明しやすいのは、あるオブジェクトのインスタンス変数などに保持されている状態。
- Reference オブジェクトを介さなくても参照できる。
- ソフト到達可能 なオブジェクト:
- 強到達可能ではないが、ソフト参照(soft reference)を介することで参照可能なオブジェクト。
- 1個以上の SoftReference と0個以上の WeakReference または PhantomReference が保持している状態。
- 弱到達可能 なオブジェクト:
- 強到達可能でもソフト到達可能でもないが、弱参照(weak reference)を介することで参照可能なオブジェクト。
- 1個以上の WeakReference と0個以上の PhantomReference が保持している状態。
- ファイナライズの対象となっている。
- ファントム到達可能 なオブジェクト:
- 強到達可能でもソフト到達可能でも弱到達可能でもないファイナライズされたオブジェクトで、ファントム参照(phantom reference)が保持しているオブジェクト。
- 1個以上の PhantomReference が保持している状態。
- 到達不可能 なオブジェクト:
- 上記のいずれによっても到達できないオブジェクト。
- 再生(reclamation)の対象となっている。
で、それぞれの参照の強さを確認するための検証コード。まぁ、言うまでもなく「強>ソフト>弱>ファントム>到達不可能」なんだけど。
import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.Date; import java.util.Set; import java.util.TreeSet; public class Main { /** * @param args */ public static void main(String[] args) { MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); memoryMXBean.setVerbose(true); { Set<String> set = new TreeSet<String>(); MyObj reference0 = new MyObj(0); /* * 変数 reference0 に保持されている MyObj オブジェクトを * 各 Reference オブジェクトに保持させてみる。 * => PhantomReference は最初から get() で参照できない。 */ { Reference<MyObj> reference = new SoftReference<MyObj>(reference0); System.out.printf(" %-16s: %s\n", reference.getClass().getSimpleName(), reference.get() != null ? "O" : "X"); } { Reference<MyObj> reference = new WeakReference<MyObj>(reference0); System.out.printf(" %-16s: %s\n", reference.getClass().getSimpleName(), reference.get() != null ? "O" : "X"); } { Reference<MyObj> reference = new PhantomReference<MyObj>(reference0, null); System.out.printf(" %-16s: %s\n", reference.getClass().getSimpleName(), reference.get() != null ? "O" : "X"); } /* * Reference オブジェクト以外に参照するものがいない MyObj オブジェクト */ Reference<MyObj> reference1 = new SoftReference<MyObj>(new MyObj(1)); Reference<MyObj> reference2 = new WeakReference<MyObj>(new MyObj(2)); Reference<MyObj> reference3 = new PhantomReference<MyObj>(new MyObj(3), null); new MyObj(4); int i = 0; System.out.printf(" %5d %5d %s %s %s %s\n", -1, set.size(), reference0 != null ? "O" : "X", reference1.get() != null ? "O" : "X", reference2.get() != null ? "O" : "X", reference3.get() != null ? "O" : "X"); for (; i < 1000; i += 8) { System.out.printf(" %5d %5d %s %s %s %s\n", i, set.size(), reference0 != null ? "O" : "X", reference1.get() != null ? "O" : "X", reference2.get() != null ? "O" : "X", reference3.get() != null ? "O" : "X"); } for (; i < 80000; i += 1000) { System.out.printf(" %5d %5d %s %s %s %s\n", i, set.size(), reference0 != null ? "O" : "X", reference1.get() != null ? "O" : "X", reference2.get() != null ? "O" : "X", reference3.get() != null ? "O" : "X"); for (int j = 0; j < 1000; ++j) { set.add(String.valueOf(i + j)); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * creation と finalization の際にログ出力するオブジェクト。 */ public static class MyObj { private int id; public MyObj(int id) { this.id = id; System.out.println(" " + new Date() + ": " + "[" + Thread.currentThread().getName() + "] MyObj(" + id + ")#<init>()"); } protected void finalize() throws Throwable { System.out.println(" " + new Date() + ": " + "[" + Thread.currentThread().getName() + "] MyObj(" + id + ")#finalize()"); super.finalize(); } } }
で、実行結果。
Fri Nov 27 23:51:02 JST 2009: [main] MyObj(0)#<init>()
[GC 505K->249K(1984K), 0.0062355 secs]
SoftReference : O
WeakReference : O
PhantomReference: X
Fri Nov 27 23:51:02 JST 2009: [main] MyObj(1)#<init>()
Fri Nov 27 23:51:02 JST 2009: [main] MyObj(2)#<init>()
Fri Nov 27 23:51:02 JST 2009: [main] MyObj(3)#<init>()
Fri Nov 27 23:51:02 JST 2009: [main] MyObj(4)#<init>()
-1 0 O O O X
0 0 O O O X
8 0 O O O X
(中略)
816 0 O O O X
[GC 761K->292K(1984K), 0.0040452 secs]
824 0 O O O X
832 0 O O X X
840 0 O O X X
Fri Nov 27 23:51:02 JST 2009: [Finalizer] MyObj(4)#finalize()
848 0 O O X X
Fri Nov 27 23:51:02 JST 2009: [Finalizer] MyObj(3)#finalize()
856 0 O O X X
Fri Nov 27 23:51:02 JST 2009: [Finalizer] MyObj(2)#finalize()
864 0 O O X X
872 0 O O X X
(中略)
992 0 O O X X
1000 0 O O X X
2000 1000 O O X X
3000 2000 O O X X
(中略)
17000 16000 O O X X
18000 17000 O O X X
[GC 1705K->1684K(2240K), 0.0070140 secs]
[Full GC 1684K->1684K(2240K), 0.0400832 secs]
Fri Nov 27 23:51:19 JST 2009: [Finalizer] MyObj(1)#finalize()
19000 18000 O X X X
20000 19000 O X X X
(中略)
28000 27000 O X X X
29000 28000 O X X X
[GC 2609K->2588K(3352K), 0.0072466 secs]
(続く)結果を見てみる。(到達不可能なオブジェクトへの参照を便宜上「無参照」と記す。)
- ファントム参照は、しょっぱなから get() が null を返す。つまり、もはや外部から参照することはできないことを表す。(もちろんそれより強い参照が別にあれば参照できる)
- ファントム参照、弱参照、無参照は、最初の GC で finalize() が呼び出される。(ファントム到達可能なオブジェクトは「ファイナライズされた」オブジェクトということだが、finalize() が呼び出されるのは GC のとき、ということらしい。まぁ、finalize() が呼び出されることに依存するコードは書くな、というのは鉄則だし)
- ソフト参照は、最初の Full GC で finalize() が呼び出される。(この辺はどこかに定義されているわけではないが、弱参照よりは強いということは言えると思う)
- 強参照は、当然のことながら finalize() が呼び出されることはない。