5

假设我有一个像这样的 C# 方法:(显然不是真正的代码)

byte[] foo()
{
    var a = MethodThatReturns500mbObject();
    var b = MethodThatReturns200mbObject(a);
    byte[] c = MethodThatReturns150mbByteArray(b);
    byte[] d = UnwiselyCopyThatHugeArray(c);
    return d;
}

正如您可以通过命名猜到的那样,这些方法返回的对象是巨大的。每个对象需要数百兆的总 RAM,尽管前两个对象由数百万个较小的对象组成,而不是像后两个数组那样是一个巨大的块。

我们将很快将其优化为流式解决方案,但与此同时,我想确保至少在执行代码以生成较晚的对象时,我们不会阻止早期对象的 GC。

我的问题是:a只要 MethodThatReturns200mbObject(a) 返回,对象是否有资格获得 GC?如果没有,让 GC 知道有 500MB 的存在等待它的最佳方法是什么?

我的问题的核心是.NET GC 对“这个对象没有引用”的判断是否足够聪明,知道返回a后不能引用MethodThatReturns200mbObject(a)。尽管var a理论上仍然可用于以后的代码,a但在方法的第二行下方的任何地方都不会引用。理论上,编译器可以让 GC 知道a没有引用。但在实践中,我不确定它的行为方式。你知道吗?

4

1 回答 1

1

这篇文章用例子解释了它。

理论上,编译器可以让 GC 知道 a 未被引用。但在实践中,我不确定它的行为方式。你知道吗?

正确答案是,对象在方法结束时是否有资格进行垃圾回收取决于项目配置。正如我什么时候需要使用 GC.KeepAlive?(这也描述了 GC.KeepAlive 的目的——简而言之,它是一种引用或“使用”变量的方法,确保优化器不会优化使用),垃圾收集器可能会决定尽快收集对象它们不再被任何执行代码使用。这很可能发生在可以访问引用(在编译时)但没有编写此类代码的情况下。

但是,在调试模式下编译和执行代码时,编译器会阻止这种情况发生以简化调试。因此,我们的测试方法的正确实现包括一个预处理器指令:

另一本好书我什么时候需要使用 GC.KeepAlive?

于 2017-07-21T00:37:31.253 回答