如何让Java系统发布软引用?

2022-09-02 13:29:50

我将使用基于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 */

答案 1

这段代码强制 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!");
}

答案 2

这项改进将适用于超过2G的最大内存。它会循环,直到发生内存不足错误。

@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));

}