我将使用基于SoftReference的缓存(一个非常简单的事情)。但是,我在为它编写测试时遇到了一个问题。
测试的目的是在内存清理发生后检查缓存是否再次从服务器请求先前缓存的对象。
在这里,我找到了如何使系统释放软引用对象的问题。调用 System.gc()是不够的,因为在内存不足之前不会释放软引用。我正在PC上运行此单元测试,因此VM的内存预算可能非常大。
==================稍后添加=========================== ===
感谢所有照顾的人!
在考虑了所有职业选手和反对者后,我决定采用 nanda 和 jarnbjo 建议的蛮力方式。然而,似乎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 */
答案 0 :(得分:13)
这段代码强制JVM刷新所有SoftReferences。这样做很快。
它比Integer.MAX_VALUE方法更好用,因为这里JVM确实试图分配那么多内存。
try {
Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()];
} catch (OutOfMemoryError e) {
// Ignore
}
我现在在需要使用SoftReferences单元测试代码的地方使用这段代码。
更新:这种方法确实只能在不到2G的最大内存下工作。
另外,需要非常小心SoftReferences。错误地保留一个硬引用很容易否定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!");
}
答案 1 :(得分:3)
一项改进,适用于超过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));
}
答案 2 :(得分:1)
这是我的小测试。根据需要进行修改。
@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);
}
}
答案 3 :(得分:0)
您可以在测试中将软引用显式设置为null,并因此模拟软引用已被释放。
这可以避免任何复杂的测试设置,即内存和垃圾收集依赖。
答案 4 :(得分:-1)
而不是长时间运行的循环(如nanda所建议的),简单地创建一个巨大的原始数组以分配比VM可用的内存更快更容易,然后捕获并忽略OutOfMemoryError:
try {
long[] foo = new long[Integer.MAX_VALUE];
}
catch(OutOfMemoryError e) {
// ignore
}
这将清除所有弱引用和软引用,除非您的VM有超过16GB的可用堆。