内存管理对最终将对象设置为null的影响

时间:2015-12-07 10:13:27

标签: c# garbage-collection

public void func1()
{    
    object obj= null;
    try
    {
             obj=new object();
    }
    finally
    {
             obj = null;
    }
}

在大型对象的内存管理方面,为finally块中的引用赋值是否有任何优势?

1 个答案:

答案 0 :(得分:3)

让我们在这里处理明确和隐含的问题。

问:首先,当您完成本地变量时,是否有必要将null分配给它?

A:不,完全没有。当使用优化编译而不是在调试器下运行时,JITter知道正在使用变量的代码段,并且当您通过该段时将自动停止将其视为根。换句话说,如果您为变量指定了某些内容,然后在某个时刻再也没有从中读取过,那么即使您没有明确地将其设置为null,也可能会收集它。

所以你的例子可以安全地写成:

public void func1()
{    
    object obj = new object();
    // implied more code here
}

如果"中没有代码,则此处隐含更多代码"永远访问obj变量,它不再被视为根。

请注意,如果在非优化程序集中运行,或者将调试程序连接到进程,则会发生更改。在这种情况下,变量的范围被人为地扩展,直到它们的范围结束,以便更容易调试。

问:其次,周围班级的字段怎么样?

A:这里肯定会有所作为。

如果方法周围的对象长时间保持活动状态,并且对字段内容的需求已经消失,则是,将字段设置为null将使其引用的旧对象有资格收藏。

所以这段代码可能有价值:

public class SomeClass
{
    private object obj;

    public void func1()
    {    
        try
        {
             obj=new object();
             // implied more code here
        }
        finally
        {
             obj = null;
        }
    }
}

但是,那你为什么要这样做呢?相反,您应该努力编写不依赖于周围状态的更清晰的代码。在上面的代码中,你应该重构"暗示更多的代码在这里"要在要使用的对象中传递,并删除全局字段。

显然,如果你不能这样做,那么是的,只要不再需要对象引用就将字段设置为null是一个好主意。

有趣的实验,如果您在LINQPad中运行以下代码并进行优化,那么您对输出的期望是什么?

void Main()
{
    var s = new Scary();
    s.Test();
}

public class Scary
{
    public Scary()
    {
        Console.WriteLine(".ctor");
    }

    ~Scary()
    {
        Console.WriteLine("finalizer");
    }

    public void Test()
    {
        Console.WriteLine("starting test");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        Console.WriteLine("ending test");
    }
}

回答(当你认为你已经得到它时,鼠标悬停显示):

  

.ctor
 开始测试
 终结
 结束测试

说明:

  

由于实例方法的隐式this参数从未在方法中使用,因此即使方法当前正在运行,也会收集该方法周围的对象。