Java 自动查找泄漏内存的JUnit测试的方法

Java 自动查找泄漏内存的JUnit测试的方法,java,unit-testing,memory-leaks,junit,Java,Unit Testing,Memory Leaks,Junit,我们问题的根源是单身。但是单例是很难打破的,同时我们有很多单元测试使用单例,而没有在tearDown()方法中小心地完全清除它们。我认为检测此类测试的一个好方法是查找内存泄漏。如果在tearDown()和System.gc()之后使用的内存大于测试启动时使用的内存,则可能是测试泄漏,也可能是类加载器加载了更多的类。有没有办法自动检测此类问题?只是想一想:如果有两个空测试紧跟在一起运行,那么第二个测试在拆卸()后不应该使用不同的内存。如果是这样,您(可能)的setup()/teardown()系统

我们问题的根源是单身。但是单例是很难打破的,同时我们有很多单元测试使用单例,而没有在tearDown()方法中小心地完全清除它们。我认为检测此类测试的一个好方法是查找内存泄漏。如果在tearDown()和System.gc()之后使用的内存大于测试启动时使用的内存,则可能是测试泄漏,也可能是类加载器加载了更多的类。有没有办法自动检测此类问题?

只是想一想:如果有两个空测试紧跟在一起运行,那么第二个测试在拆卸()后不应该使用不同的内存。如果是这样,您(可能)的setup()/teardown()系统中的某个地方有漏洞。

我认为这不是一个好方法
System.gc()
不能保证像您认为的那样完全清除任何未使用的对象


如果您的根本问题是单元测试最终使用全局数据(单例),而没有正确清理它们,那么您应该解决根本问题:这些单元测试。查找所有未使用
tearDown()
的测试,或者查找所有使用特定单例的测试应该不会太难。

您是否可以在TestCase和您的单个测试类之间引入一个子类来进行清理?然后子类将只负责调用super.teardown(),并且只负责调用那些拥有自己的teardown()的子类。

如果您的单例只打算初始化一次,您可以使用代码检查是否重新初始化,并在检测到时记录当前堆栈。然后,如果您检查堆栈,您将看到哪个测试得到了成功,并且您可以检查JUnit日志以查看在此之前运行的测试是什么

为了更彻底地解决这个问题,我建议您使用一个单例初始值设定项来记住它初始化的内容,并使用一个撕裂方法来撕裂它初始化的所有内容,而不是检测它。这样的话,测试只能通过这个类进行初始化,并且只需要在teardown中做一件事


我还认为Carl Manaster的建议很好,但是如果您使用JUnit4,那么您可以有一个在超类中运行的拆卸方法,而不必记住调用super。除非您使用JUnit3 GUI,否则JUnit4应该是替代品。唯一要做的就是利用它的新特性,你必须迁移整个测试,你不能让两者都生活在同一个类中。因此,与这些单例交互的测试必须一次迁移一个完整的测试类。

我完全同意其他人的观点,即监视内存使用情况并不是跟踪这个系统的可行方法。gc()不会像您预期的那样运行,或者以足够的精度来实现您的目标

您将需要一个工具来检查引用图并显示分配调用堆栈

我使用了Borland和ej technologies的OptimizeIt,两者都取得了成功(谷歌快速搜索显示OptimizeIt可能已经死了)

对于这个特定的问题,还可以使用来组合一个更好的监视器


编辑:Wierd,但当我回顾这个答案时,我接到了Embarcadero的电话,他显然已经购买了OptimizeIt,进行了一些更新,现在正在以该名称进行营销。

您可以使用自动分析每次测试后获取的堆转储,或者在所有测试后可能更好。MAT可以相当自动地发现内存泄漏

由于junit现在必须记录额外的测试结果,所以总堆大小不会在测试运行之间的某个地方增加吗?只要没有运行垃圾收集,第一次运行测试所使用的内存就不会被回收。找到它们一点也不难。有成千上万的。它们中的大多数调用Singleton.getInstance().reset()或其他可以缓解问题的方法。可能有一些测试没有,而且很难找到。是时候摆脱所有单身汉了:)理论上我同意,但这需要几年的时间。这是一个1000万行的代码库,很难在小块中重构单个代码。