【问题标题】:Automated memory leak detection in JavaJava中的自动内存泄漏检测
【发布时间】:2011-10-08 15:52:23
【问题描述】:

我正在考虑对 Java 程序进行自动内存泄漏检测。基本算法是创建包含以下逻辑的 JUnit:

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框架使用起来很方便。

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

【问题讨论】:

  • 我曾经也想过这样的测试,但找不到好的解决方案..希望有人能来这里。

标签: java unit-testing testing memory-leaks 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 Tip 130: Do you know your data size?)

如果您想尝试一下,这是完整的帮助类:

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

}

【讨论】:

  • 这是我在网上能找到的最好的东西。我会立即接受它:)
  • @Andy,为什么 Thread.sleep() 是必要的?继续尝试垃圾收集还不够吗?
  • Thread.sleep 的目的是给垃圾收集器一点时间赶上。如果代码没有休眠,它将处于一个紧密循环中,这将锁定 CPU,直到垃圾收集器完成。
【解决方案2】:

这在 Java 中不是一种有意义的方法。 System.gc() 不给你任何合理的保证,即使你说服自己有问题,这种方法也无助于你发现问题。

您应该改用内存分析器。如果您不想为专业的付费,您可以尝试与 JVM 一起安装的jvisualvm

【讨论】:

  • JConsole 随 JDK 一起提供,我认为它对于大多数用例来说已经足够了。
  • JConsole 真的很棒! Netbeans 还附带了方便的分析工具。
  • 我认为 Netbeans profiler 和 JConsole/jvisualvm 是同一个工具,后者只是独立于 Netbeans。
  • 一次“分析和修复”并不能保护您将来在同一个地方免受问题的影响。单元测试一劳永逸地检测、揭示和修复问题。
  • @yegor256 你到底怎么写单元测试来测试内存泄漏?不,单元文本本身并不能解决问题。这没有任何意义……但感谢您对我 2 年前的帖子感兴趣。
【解决方案3】:

至少您必须在进行测试之前先运行测试,以消除只需要创建一次的一次性对象。这样做:

test()
checkMem()
test()
checkMem()
compareIfMemUsageHasNotIncreased()

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2012-07-16
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-22
      • 2015-02-28
      相关资源
      最近更新 更多