【问题标题】:Does inlining affect garbage collection?内联会影响垃圾回收吗?
【发布时间】:2017-06-26 19:38:13
【问题描述】:

假设一个函数接受一个对象列表:

void WriteData(List<LargeObject> objectsToWrite);

如果我们调用这个函数,为了可读性或调试,我们可能会考虑将其设为局部变量:

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

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

WriteData(SomeMethodThatPreparesTheObjects());

这两者在功能上是等效的——但我的问题是:这两种方法是否都保留对象列表直到方法执行结束(因为有一个局部变量将是 GC 根)——或者它是否取决于编译器内部关于后一个调用是否最终被翻译成局部变量?

【问题讨论】:

  • 列表(或任何对象)将存储在堆上。它与您如何编写代码无关,无论您使用字段、变量还是内联函数。
  • 局部变量不会阻止对象被收集。当不再使用该变量时 - 可以收集对象,甚至在方法完成之前。因此,在方法执行结束之前,您使用的任何方法都不会保留对象。
  • 请注意,在某些情况下您希望一个对象保持活动状态;正如上面的评论正确指出的那样,优化器可以提前杀死本地人。如果您必须保持本地活动,请使用KeepAlive 函数。有关详细信息,请参阅stackoverflow.com/a/15388788/88656
  • 另请注意,还有第三个选项:WriteData(objectsToWrite: SomeMethodThatPreparesTheObjects()); 这会生成与WriteData(SomeMethodThatPreparesTheObjects()); 相同的IL。
  • @EricLippert - 感谢您的链接 - 这非常有帮助

标签: c# garbage-collection


【解决方案1】:

首先,在最后一次使用该变量之后,即使当前正在运行的方法中的局部变量对它的引用,对象也可以被垃圾回收。但是,如果您在没有优化的情况下构建(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引用.

【讨论】:

  • 太棒了 - 因为代码示例演示它而接受。
【解决方案2】:

您可能无法区分。当 .NET 感觉到内存压力时,它会收集、遍历对象寻找根等。

如果发生在执行WriteData()的过程中,大对象将不会被收集,因为它正在使用中。

如果您使用局部变量,编译器会在调用 WriteData() 后检测到它不再被使用,因此它不会被“root”——该变量本质上是“超出范围”。我想在您设置局部变量和调用 WriteData() 之间发生收集的可能性很小,但在这种情况下,您不希望收集它 - 您将要使用它。

无论如何,“相信原力”——正确的事情总会发生!

【讨论】:

  • 谢谢 - 您是否碰巧有任何链接,该链接描述了在方法中的最后一次引用之后从范围中删除变量的行为?
  • @MattWhitfield 我做了一些谷歌搜索,发现了几十篇关于范围的文章,但我认为你也对编译器优化感兴趣,因为在这种情况下,这两件事都在起作用 - C# 对您的代码可以确定您的变量不再需要,因此可以删除,可以这么说。这会产生 GC 的影响,但这是优化的副作用。
猜你喜欢
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 2010-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-13
相关资源
最近更新 更多