使用WeakReference进行内存泄漏单元测试时出现奇怪行为 [英] Strange behaviour while Unit testing for memory leak using WeakReference

查看:137
本文介绍了使用WeakReference进行内存泄漏单元测试时出现奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图编写一个单元测试来测试内存泄漏。
重现步骤:

  TestClass test = new TestClass(); 
WeakReference reference = new WeakReference(test,true);

test = null;

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Debug.Assert(reference.Target == null,Memory Leak); //这工作

//用以下代替上面的行,我看到内存泄漏被打印。

if(reference.Target!= null)
{
Console.WriteLine(Memory Leak);
}

我添加了一个终结器:

 〜TestClass(){Console.WriteLine(TestClass instance finalized);} 

,我注意到终结器在Assert情况下作为GC命令的一部分被调用,但是当我用if条件替换它时,终结器不会作为GC命令的一部分被调用,因此该参考的目标仍然存在。



预期行为:

  if(reference.Target!= null)Console.WriteLine(Memory Leak); 

应该可以。 p

实际行为:

  Debug.Assert(reference.Target == null,Memory Leak); 

有效,但

  if(reference.Target!= null)Console.WriteLine(Memory Leak); 

不起作用,因为它会打印内存泄漏

解决方案

我找到了这个问题的根本原因。这段代码在发布构建配置中将按预期工作,但不在调试中(这正是我正在运行的版本)。



在Debug案例中,测试不是GCed,因为存在具有属性Target的成员引用,该引用持有对test对象的引用。为了能够使用诸如监视窗口之类的调试器工具来查看它,编译器保持活动状态。如果你摆脱了WeakReference实例(以及相应的条件),即使在调试模式下,你也会看到它被GC'ed。而且,如果在Debug.Assert中使用引用,它似乎并不包含对target的引用,并且使得test可以被GC化。



<在Release模式下,测试是GC'ed的原因,因为编译器JIT编译代码并摆脱测试变量(因为它反正分配给null),并且没有办法引用它代码中的任何地方。这使它成为GC'ed。由于引用是对测试对象的弱引用,因此它不会保留它,并允许它被GC化,因此if条件在释放模式下工作。


I'm trying to write a unit test for testing a memory leak. Steps to Reproduce:

    TestClass test = new TestClass();
    WeakReference reference = new WeakReference(test, true);

    test = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    Debug.Assert(reference.Target == null, "Memory Leak"); // This works

     //Replacing the above line with following, I see "Memory Leak" being printed.

    if (reference.Target != null)
    {
        Console.WriteLine("Memory Leak");
    }

I added a finalizer:

~TestClass() { Console.WriteLine("TestClass instance finalized");}

and I noticed that the finalizer gets called as part of GC commands in the Assert case but when I replace it with if condition, the finalizer doesn't gets called as part of the GC commands and hence the reference's target is still alive. The finalizer gets called only before the program exists.

Expected Behavior:

if(reference.Target != null) Console.WriteLine("Memory Leak");

should work.

Actual Behavior:

Debug.Assert(reference.Target == null, "Memory Leak");

works but

if(reference.Target != null) Console.WriteLine("Memory Leak");

doesn't works as it prints "Memory Leak"

解决方案

I found the root cause of this issue. This code will work as expected in the Release build configuration but not in debug(which is what I was running).

In the Debug case, the reason why "test" is not GCed because there exists a member "reference" which has a property Target that holds a reference to the "test" object. In order to be able to view this using debugger tools like Watch window, the compiler keeps it alive. If you get rid of the WeakReference instance(and corresponding if condition), you will see it being GC'ed even in Debug mode. Also, it seems that if the "reference" is used in Debug.Assert, it doesn't holds a reference to target and enables "test" to be GC'ed.

In the Release mode, the reason why "test" is GC'ed because the compilers JIT compiles the code and gets rid of the "test" variable(since it is anyways assigned to null) and there is no way to reference it anywhere in the code. That enables it to be GC'ed. Since "reference" is a weak reference to the "test" object, it will not hold it and will allow it to be GC'ed and hence the if condition works in Release mode.

这篇关于使用WeakReference进行内存泄漏单元测试时出现奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆