假设一个函数需要一个对象列表:

void WriteData(List<LargeObject> objectsToWrite);


如果我们正在调用此函数,则出于可读性或调试目的,我们可以考虑将其设置为局部变量:

var objectsToWrite = SomeMethodThatPreparesTheObjects();
WriteData(objectsToWrite);


但是,我们也可以内联变量,以使函数调用变为:

WriteData(SomeMethodThatPreparesTheObjects());


两者在功能上是等效的-但我的问题是:这两种方法都将对象列表保留到方法执行结束之前(因为存在一个本地变量,它将成为GC根)-还是取决于编译器的内部结构?后一个调用是否最终被转换为局部变量?

最佳答案

首先,即使该对象的最后一次使用之后,即使该对象在当前运行的方法中从本地变量引用也可以被垃圾回收。但是,如果构建时没有优化(Visual Studio中的“优化代码”选项或csc.exe中的类似选项),则局部变量将在方法的整个生命周期内生成根,因此调试代码时不会遇到麻烦。因此,在这种情况下,您提出的问题的两种方法之间存在差异。

在启用优化的情况下进行编译时-局部变量将不会阻止从objectsToWrite返回后立即收集WriteData,因此在这方面没有区别。在这种特殊情况下,编译器很可能会完全消除局部变量,因此,两种情况下的编译代码都是相同的(同样,使用优化进行编译时)。

您可以使用以下简单代码测试在方法结束之前可以收集局部变量引用的对象:

static void Main(string[] args) {
    var ob = new object();
    Write(ob);
    var wr = new WeakReference(ob);
    GC.Collect(2, GCCollectionMode.Forced);
    Console.WriteLine(wr.IsAlive);
    Console.ReadKey();
}

static void Write(object ob) {
    Console.WriteLine(ob);
}


使用优化进行编译,无需调试器即可运行,您将看到弱引用已失效,这意味着即使方法Main仍在运行并且对象由局部变量ob引用,该弱引用所引用的对象也已被收集。

10-08 13:44