AssertGC
WeakReference使ったキャッシュとかのテストとかで使ってます。
System.gc()は別にGC走らせなくてもよかったり、いろいろと問題はありますが、インクリメンタルGC以外を指定して下のようなコード書けばほぼOKだと思います。
本気でやるなら、WeakReferenceの回収スレッドとGCを全力で用意する必要があります。
厳密じゃない動作ということで、参考までに。
public class Assert2 { private static final int ALLOCATE_SIZE = 65536; public static void assertGc(WeakReference<?> ref) { SoftReference<LinkedList<int[]>> memoryEater1 = new SoftReference<LinkedList<int[]>>(new LinkedList<int[]>()); SoftReference<LinkedList<int[]>> memoryEater2 = new SoftReference<LinkedList<int[]>>(new LinkedList<int[]>()); while (true) { System.gc(); if (consumeMemory(memoryEater1)) { break; } if (consumeMemory(memoryEater2)) { break; } } if (ref.get() != null) { throw new AssertionError(); } } private static boolean consumeMemory(SoftReference<LinkedList<int[]>> eater) { LinkedList<int[]> list = eater.get(); if (list == null) { return true; } list.add(new int[ALLOCATE_SIZE]); return false; } }
ポイントは、SoftReferenceを2本用意して、交互にメモリを食いつぶすオブジェクトを追加していく感じです。
1本でやろうとするとスタックにLinkedListがある状態でnew int[] するので、そこでOutOfMemoryが発生する場合があります。
public class Assert2Test { /** * Test method for {@link Assert2#assertGc(java.lang.ref.WeakReference)}. */ @Test public void testAssertGc() { Object obj = new String("Hello, world!"); WeakReference<Object> ref = new WeakReference<Object>(obj); // keeps only weak references obj = null; Assert2.assertGc(ref); } /** * Test method for {@link Assert2#assertGc(java.lang.ref.WeakReference)}. */ @Test public void testAssertGcLeak() { Object obj = new String("Hello, world!"); WeakReference<Object> ref = new WeakReference<Object>(obj); // keeps strong references // obj = null; Assert2.assertGc(ref); // failure } }
ちなみに、"Hello, world!"ではなくnew String("Hello, world!")にしてるのは、コンスタントプールにStringオブジェクトがリーク?するためです。