假设一个获取对象列表的函数:
void WriteData(List<LargeObject> objectsToWrite);
如果我们调用此函数,为了便于阅读或调试,我们可能会考虑将其作为局部变量:
var objectsToWrite = SomeMethodThatPreparesTheObjects();
WriteData(objectsToWrite);
但是,我们也可以内联变量,使函数调用成为:
WriteData(SomeMethodThatPreparesTheObjects());
这两个在功能上是等价的 - 但我的问题是:两个方法都保持对象列表,直到方法执行结束(因为有一个局部变量是GC根) - 或者它依赖于编译器内部关于后一个调用是否最终被转换为局部变量?
答案 0 :(得分:3)
首先,在最后一次使用该变量之后,即使在当前运行的方法中从本地变量引用了对象,也可以对对象进行垃圾回收。但是,如果您在没有优化的情况下构建(&#34;优化代码&#34; 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
仍在运行且对象由局部变量{引用} {1}}。
答案 1 :(得分:1)
你可能无法区分它们。当.NET感觉到内存压力时,它会收集,寻找根等的行走对象
如果它在执行WriteData()的过程中发生,则大对象不会被收集,因为它正在使用中。
如果使用局部变量,编译器会在调用WriteData()之后检测到它不再被使用,因此它不会被&#34; rooted&#34; - 变量基本上超出了范围。&#34;我想在你设置本地变量和调用WriteData()之间收集的可能性很小,但是在这种情况下你不会想要收集它 - 你将要使用它
无论如何,&#34;相信力量&#34; - 正确的事情会发生!