Java 中的自动内存泄漏检测

我正在考虑为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框架使用起来很方便。

你认为这种方法有效吗?您认为这种方法在识别内存泄漏方面会成功吗?你做过这样的事情吗?


答案 1

我为内存泄漏开发了一个简单的单元测试框架,它为我可靠地工作。基本思想是创建一个对应该被垃圾回收的对象的弱引用,执行测试,执行完整的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");
}

这里有一些注意事项:

  1. 至关重要的是,您希望收集的对象不应存储在单元测试的变量中,因为它将保留到测试结束时。
  2. 这是一种有用的回归测试技术,其中已报告泄漏,并且您知道应该删除哪个对象。
  3. 这种方法的一个问题是很难确定测试失败的原因。此时,您将需要一个内存分析器(我偏爱 YourKit)。但是,IMO进行回归测试仍然很有用,这样将来就不会意外地重新引入泄漏。
  4. 我遇到了一些线程问题,并非所有引用都会立即清除,因此该方法现在尝试在失败之前多次执行GC(如本文所述:Java提示130:您知道您的数据大小吗?)

以下是完整的帮助程序类,以防您想试用它:

/**
 * 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());
}

}


答案 2

你不能用java做到这一点。垃圾回收器将在确定有必要时运行。除此之外,它可能会“释放”内存,以便可以重复使用,但这并不意味着它会解除分配块。


推荐