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。而且做起来非常快。

它比 Integer.MAX_VALUE 方法工作得更好,因为这里 JVM 真的试图分配那么多内存。

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

我现在在需要使用 SoftReferences 对代码进行单元测试的任何地方都使用这段代码。

更新:这种方法确实只适用于小于 2G 的最大内存。

此外,需要非常小心使用 SoftReferences。很容易错误地保留硬引用,这将否定软引用的效果。

这是一个简单的测试,显示它每次都在 OSX 上工作。有兴趣了解 JVM 在 Linux 和 Windows 上的行为是否相同。


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

An improvement that will work for more than 2G max memory. It loops until an OutOfMemory error occurs.

@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 有超过 16GB 的可用堆。

于 2010-09-24T09:59:18.967 回答