如何让java系统释放软引用?

发布于 2024-09-25 09:04:25 字数 851 浏览 1 评论 0原文

我将使用基于 SoftReference 的缓存(本身就非常简单)。然而,我在为其编写测试时遇到了一个问题。

测试的目的是检查内存清理发生后,缓存是否确实再次从服务器请求先前缓存的对象。

这里我发现了如何让系统释放软引用对象的问题。调用 System.gc() 是不够的,因为直到内存不足时软引用才会被释放。我在 PC 上运行此单元测试,因此虚拟机的内存预算可能相当大。

==================稍后添加================================

谢谢各位热心解答的人!

在考虑了所有的优点和缺点之后,我决定按照 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 */

I'm going to use a SoftReference-based cache (a pretty simple thing by itself). However, I've came across a problem when writing a test for it.

The objective of the test is to check if the cache does request the previously cached object from the server again after the memory cleanup occurs.

Here I find the problem how to make system to release soft referenced objects. Calling System.gc() is not enough because soft references will not be released until the memory is low. I'm running this unit test on the PC so the memory budget for the VM could be pretty large.

================== Added later ==============================

Thank you all who took care to answer!

After considering all pro's and contra's I've decided to go the brute force way as advised by nanda and jarnbjo. It appeared, however, that JVM is not that dumb - it won't even attempt garbage collecting if you ask for a block which alone is bigger than VM's memory budget. So I've modified the code like this:

    /* 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 */

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦明 2024-10-02 09:04:25

这段代码强制 JVM 刷新所有软引用。而且做起来非常快。

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

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

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

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

另外,使用软引用时需要非常小心。很容易错误地保留硬引用,从而抵消软引用的效果。

这是一个简单的测试,显示它每次都能在 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!");
}

This piece of code forces the JVM to flush all SoftReferences. And it's very fast to do.

It's working better than the Integer.MAX_VALUE approach, since here the JVM really tries to allocate that much memory.

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

I now use this bit of code everywhere I need to unit test code using SoftReferences.

Update: This approach will indeed work only with less than 2G of max memory.

Also, one need to be very careful with SoftReferences. It's so easy to keep a hard reference by mistake that will negate the effect of SoftReferences.

Here is a simple test that shows it working every time on OSX. Would be interested in knowing if JVM's behavior is the same on Linux and 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!");
}
幻想少年梦 2024-10-02 09:04:25

这项改进适用于最大内存超过 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));

}

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

}
余生再见 2024-10-02 09:04:25
  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);
  }
}
  1. Set the parameter -Xmx to a very
    small value.
  2. Prepare your soft
    reference
  3. Create as many object as
    possible. Ask for the object everytime until it asked the object from server again.

This is my small test. Modify as your need.

@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);
  }
}
一袭白衣梦中忆 2024-10-02 09:04:25

您可以在测试中显式地将软引用设置为 null,并因此模拟软引用已被释放。

这避免了任何依赖于内存和垃圾收集的复杂测试设置。

You could explicitly set the soft reference to null in your test, and as such simulate that the soft reference has been released.

This avoids any complicated test setup that is memory and garbage collection dependend.

泅人 2024-10-02 09:04:25

与长时间运行的循环(如 nanda 建议的那样)不同,简单地创建一个巨大的原始数组来分配比虚拟机可用的内存更多的内存可能更快更容易,然后捕获并忽略 OutOfMemoryError:

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

这将清除所有弱引用和软引用,除非您的 VM 有超过 16GB 的可用堆。

Instead of a long running loop (as suggested by nanda), it's probably faster and easier to simply create a huge primitive array to allocate more memory than available to the VM, then catch and ignore the OutOfMemoryError:

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

This will clear all weak and soft references, unless your VM has more than 16GB heap available.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文