如何让java系统发布Soft References?

我将使用基于SoftReference的缓存(一个非常简单的事情)。 但是,我在为它编写测试时遇到了一个问题。

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

在这里,我找到了如何使系统释放软引用对象的问题。 调用System.gc()是不够的,因为在内存不足之前不会释放软引用。 我在PC上运行此unit testing,因此VM的内存预算可能非常大。

==================后来添加==============================

谢谢所有照顾的人!

在考虑了所有职业选手和反对者之后,我决定按照nandajarnbjo的建议采取蛮力方式。 然而,似乎JVM并不是那么愚蠢 – 如果你要求一个比VM的内存预算更大的块,它甚至都不会尝试垃圾收集。 所以我修改了这样的代码:

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

这段代码强制JVM刷新所有SoftReferences。 这样做很快。

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

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

我现在在需要使用SoftReferencesunit testing代码的地方使用这段代码。

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

另外,需要非常小心SoftReferences。 错误地保留一个硬引用很容易否定SoftReferences的影响。

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

 for (int i = 0; i < 1000; i++) { SoftReference softReference = new SoftReferencelt(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!"); } 

一项改进,适用于超过2G的最大内存。 它循环,直到出现OutOfMemory错误。

 @Test public void shouldNotHoldReferencesToObject() { final SoftReference reference = new SoftReference( ... ); // Sanity check assertThat(reference.get(), not(equalTo(null))); // Force an OoM try { final ArrayList allocations = new ArrayList(); 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)); } 
  1. 将参数-Xmx设置为非常小的值。
  2. 准备你的软参考
  3. 尽可能多地创建对象。 每次询问对象,直到它再次从服务器询问对象。

这是我的小测试。 根据需要进行修改。

 @Test public void testSoftReference() throws Exception { Set s = new HashSet(); SoftReference sr = new SoftReference(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); } } 

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

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

而不是长时间运行的循环(如nanda所示),简单地创建一个巨大的原始数组以分配比VM可用的内存更快更容易,然后捕获并忽略OutOfMemoryError:

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

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