在我的代码中,在运行时创建和删除了许多对象,这些对象可以有很长的引用链(即可以从许多地方进行垃圾收集)。
在我的单元测试中,我想验证当我“删除”某个对象时,例如从主要包含它的列表中删除它,删除对该对象的所有引用。要明确:我想验证在我的应用程序中不再存在对此对象的引用。
我怎样才能做到这一点?
到目前为止,我只对此进行了测试(测试PhantomReference已入队):
@Test
public void test_objectX_can_be_garbage_collected() throws Exception {
ReferenceQueue<MyClass> refsToBeDeleted = new ReferenceQueue<>();
MyClass obj = new MyClass();
PhantomReference<MyClass> phantomRef = new PhantomReference<MyClass>(obj, refsToBeDeleted);
obj = null; // delete last strong reference to obj
System.out.println("Reference is enqueued:" + phantomRef.isEnqueued()); // "false"
System.gc(); // invoke garbage collection
Thread.sleep(1000L); // required - see text
System.gc(); // invoke garbage collection again
System.out.println("Reference is enqueued:" + phantomRef.isEnqueued()); // true
}
与
public static class MyClass {
}
doc说:
public static void gc()
运行垃圾收集器。 调用gc方法表明Java虚拟机花费了大量精力来回收未使用的对象,以使其当前占用的内存可用于快速重用。 当控制从方法调用返回时,Java虚拟机已尽最大努力从所有丢弃的对象中回收空间。
(强调我的)
这是非常含糊的(“尽力而为”)并且似乎非常不确定,例如我必须等待1秒钟再次拨打gc()
,或者拨打电话,例如连续四次将参考队列入队列。
有没有办法正确地做到这一点?我想测试对象不会持久存在,并且最终会在几个正常运行时间之后充斥我的记忆。
说明:
1。)我知道你不应该致电gc()
,因为这是错误的代码。没错,但是我从测试代码中调用它来验证生产代码。
2。)我知道gc()
从本地的角度来看实际上不是确定性的。我知道这一点并要求更好的选择。
修改
我意识到这个可以是一种“特殊的”单元测试:如果它对于给定的代码库成功一次,我知道这个引用是垃圾收集的;如果单元测试失败,可能是因为gc()
不想删除它,或者它无法删除。因此,此测试可以验证内存是否已释放,但可能需要多次尝试才能进行验证。但是,这是“特殊的”(读作:“坏”),因为我必须接受随机失败的单元测试。
答案 0 :(得分:0)
AFAIK无法确保对象已被垃圾回收。调用System.gc()
通常会导致垃圾回收,但并非总是如此。
我还有一个无限运行的Java进程 - 我在每次迭代开始时调用System.gc()
并记录所产生的已用内存量。如果这个数量随着时间的推移没有显着变化,我可以合理地确保不会“泄漏”记忆。
答案 1 :(得分:0)
我观察到当触发堆转储时,它通常会执行完整的GC。如果这是JVM在Heap集合期间强制GC的策略,那么您可以通过
触发堆集合kill -3 pid
看看它是否有效。
您可能需要添加jvm参数以获取堆转储。
答案 2 :(得分:0)
我没有看到使用引用和System.gc()
进行测试的问题(虽然我更喜欢弱引用而不是幻影引用)。我不记得这种方法有任何稳定性问题。
仅当您可以参考可能泄露的对象时,基于参考的方法才有效。
我有来自NetBeans的头部转储分析库的forked version,我用它来自动化某些类型的内存使用报告。
对于测试代码,可以对其自身进行堆转储,并检查此转储断言给定类型的特定对象数量。
Here你可以找到这种方法的例子。