18

SoftReference ベースのキャッシュを使用します (それ自体は非常に単純です)。しかし、テストを書いているときに問題に遭遇しました。

このテストの目的は、メモリのクリーンアップが発生した後、キャッシュ以前にキャッシュされたオブジェクトをサーバーから再度要求するかどうかを確認することです。

ここで、ソフト参照オブジェクトを解放するシステムを作成する方法の問題を見つけます。メモリが少なくなるまでソフト参照は解放されないため、System.gc()を呼び出すだけでは不十分です。この単体テストは PC で実行しているため、VM のメモリ バジェットはかなり大きくなる可能性があります。

================== 後ほど追記 ==============================

回答してくださった皆様、ありがとうございました!

すべての長所と短所を検討した後、 nandajarnbjoのアドバイスに従って、力ずくで行くことにしました。ただし、JVM はそれほど愚かではないように見えます。VM のメモリ バジェットを単独で超えるブロックを要求しても、ガベージ コレクションを試みません。そこで、次のようにコードを修正しました。

    /* Force releasing SoftReferences */
    try {
        final List<long[]> memhog = new LinkedList<long[]>();
        while(true) {
            memhog.add(new long[102400]);
        }
    }
    catch(final OutOfMemoryError e) {
        /* At this point all SoftReferences have been released - GUARANTEED. */
    }

    /* continue the test here */
4

5 に答える 5

15

このコードは、JVMにすべてのSoftReferenceを強制的にフラッシュさせます。そして、それは非常に高速です。

ここではJVMが実際にその量のメモリを割り当てようとするため、Integer.MAX_VALUEアプローチよりもうまく機能しています。

try {
    Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()];
} catch (OutOfMemoryError e) {
    // Ignore
}

私は今、SoftReferencesを使用してテストコードを単体テストする必要があるすべての場所でこのコードを使用しています。

更新:このアプローチは、実際には2G未満の最大メモリでのみ機能します。

また、SoftReferencesには十分注意する必要があります。SoftReferencesの効果を無効にする、誤ってハードリファレンスを保持するのは非常に簡単です。

これは、OSXで毎回動作することを示す簡単なテストです。LinuxとWindowsでJVMの動作が同じかどうかを知りたいと思います。


for (int i = 0; i < 1000; i++) {
    SoftReference<Object> softReference = new SoftReferencelt<Object>(new Object());
    if (null == softReference.get()) {
        throw new IllegalStateException("Reference should NOT be null");
    }

    try {
        Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()];
    } catch (OutOfMemoryError e) {
        // Ignore
    }

    if (null != softReference.get()) {
        throw new IllegalStateException("Reference should be null");
    }

    System.out.println("It worked!");
}
于 2010-09-28T06:33:41.593 に答える
4

2G以上の最大メモリで機能する改善。OutOfMemoryエラーが発生するまでループします。

@Test
public void shouldNotHoldReferencesToObject() {
    final SoftReference<T> reference = new SoftReference<T>( ... );

    // Sanity check
    assertThat(reference.get(), not(equalTo(null)));

    // Force an OoM
    try {
        final ArrayList<Object[]> allocations = new ArrayList<Object[]>();
        int size;
        while( (size = Math.min(Math.abs((int)Runtime.getRuntime().freeMemory()),Integer.MAX_VALUE))>0 )
            allocations.add( new Object[size] );
    } catch( OutOfMemoryError e ) {
        // great!
    }

    // Verify object has been garbage collected
    assertThat(reference.get(), equalTo(null));

}
于 2011-05-24T16:47:19.663 に答える
1
  1. パラメータ -Xmx を非常に小さい値に設定します。
  2. ソフトリファレンスを準備する
  3. できるだけ多くのオブジェクトを作成します。サーバーからオブジェクトを再度要求するまで、毎回オブジェクトを要求します。

これは私の小さなテストです。必要に応じて変更してください。

@Test
public void testSoftReference() throws Exception {
    Set<Object[]> s = new HashSet<Object[]>();

    SoftReference<Object> sr = new SoftReference<Object>(new Object());

    int i = 0;

    while (true) {
        try {
            s.add(new Object[1000]);
        } catch (OutOfMemoryError e) {
            // ignore
        }
        if (sr.get() == null) {
            System.out.println("Soft reference is cleared. Success!");
            break;
        }
        i++;
        System.out.println("Soft reference is not yet cleared. Iteration " + i);
  }
}
于 2010-09-24T09:41:42.760 に答える
0

テストでソフト参照を明示的に null に設定し、ソフト参照が解放されたことをシミュレートできます。

これにより、メモリとガベージ コレクションに依存する複雑なテスト セットアップが回避されます。

于 2010-09-24T09:27:26.007 に答える
-1

(nanda が示唆するように) 実行時間の長いループの代わりに、単純に巨大なプリミティブ配列を作成して、VM が使用できるよりも多くのメモリを割り当て、OutOfMemoryError をキャッチして無視する方がおそらく高速で簡単です。

    try {
        long[] foo = new long[Integer.MAX_VALUE];
    }
    catch(OutOfMemoryError e) {
        // ignore
    }

これにより、VM で 16 GB を超えるヒープが使用可能でない限り、すべての弱参照とソフト参照がクリアされます。

于 2010-09-24T09:59:18.967 に答える