我在考虑Java程序的自动内存泄漏检测。基本算法是创建包含以下逻辑的JUnits:
Call System.gc() several times
Determine initial heap memory consumption using either Runtime class or JMX
Loop
Do something that exercises program under test
End loop
Call System.gc() several times
Determine final heap memory consumption
Compare initial and final memory numbers
循环用于查看内存是否以小增量爬升。
有必要区分内存使用的预期和意外增加。
这不是一个单元测试。但是JUnit框架使用起来很方便。
您认为这种方法有效吗? 您是否认为这种方法可以成功识别内存泄漏? 你做过这样的事吗?
答案 0 :(得分:18)
我为内存泄漏开发了一个简单的单元测试框架,这对我来说是可靠的。基本思想是创建一个对象的弱引用,该对象应该被垃圾收集,执行测试,执行完整的GC,然后验证弱引用是否已被清除。
这是一个使用我的框架的相当典型的回归测试:
public void testDS00032554() throws Exception {
Project testProject = getTestProject();
MemoryLeakVerifier verifier = new MemoryLeakVerifier(new RuntimeTestAction(getTestClassMap()));
testProject.close();
verifier.assertGarbageCollected("RuntimeTestAction should be garbage collected when project closed");
}
这里有一些注意事项:
如果您想尝试一下,这是完整的助手类:
/**
* A simple utility class that can verify that an object has been successfully garbage collected.
*/
public class MemoryLeakVerifier {
private static final int MAX_GC_ITERATIONS = 50;
private static final int GC_SLEEP_TIME = 100;
private final WeakReference reference;
public MemoryLeakVerifier(Object object) {
this.reference = new WeakReference(object);
}
public Object getObject() {
return reference.get();
}
/**
* Attempts to perform a full garbage collection so that all weak references will be removed. Usually only
* a single GC is required, but there have been situations where some unused memory is not cleared up on the
* first pass. This method performs a full garbage collection and then validates that the weak reference
* now has been cleared. If it hasn't then the thread will sleep for 50 milliseconds and then retry up to
* 10 more times. If after this the object still has not been collected then the assertion will fail.
*
* Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
*/
public void assertGarbageCollected(String name) {
Runtime runtime = Runtime.getRuntime();
for (int i = 0; i < MAX_GC_ITERATIONS; i++) {
runtime.runFinalization();
runtime.gc();
if (getObject() == null)
break;
// Pause for a while and then go back around the loop to try again...
try {
EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing
Thread.sleep(GC_SLEEP_TIME);
} catch (InterruptedException e) {
// Ignore any interrupts and just try again...
} catch (InvocationTargetException e) {
// Ignore any interrupts and just try again...
}
}
PanteroTestCase.assertNull(name + ": object should not exist after " + MAX_GC_ITERATIONS + " collections", getObject());
}
}
答案 1 :(得分:7)
你无法用java做到这一点。垃圾收集器将在确定必要时运行。除此之外,它可以“释放”内存,以便可以重用它,但这并不意味着它将释放块。
答案 2 :(得分:4)
这在Java中不是一种有意义的方法。 System.gc()
没有给你任何合理的保证,即使你说服自己存在问题,这种方法也无法帮助你找到问题。
您应该使用内存分析器。如果您不想支付专业费用,可以尝试与JVM一起安装的jvisualvm
。
答案 3 :(得分:0)
至少在进行测试之前必须先运行测试,以便释放必须只创建一次的一次性对象。所以:
test()
checkMem()
test()
checkMem()
compareIfMemUsageHasNotIncreased()