符合条件的C#编译器是否可以优化本地(但未使用)变量(如果它是对象的唯一强引用)?

另请参阅以下相关资源:

  • .NET垃圾收集器是否执行代码的预测分析? (在Stack Overflow上)
  • WP7:GC何时将局部变量视为垃圾 (MSDN上的博客文章)

换一种说法:

可以在变量超出范围之前回收局部变量引用的对象(例如,因为变量已分配,但之后不再使用),或者该变量在变量退出之前保证不符合垃圾收集条件。范围?

让我解释:


void Case_1() { var weakRef = new WeakReference(new object()); GC.Collect(); // <-- doesn't have to be an explicit call; just assume that // garbage collection would occur at this point. if (weakRef.IsAlive) ... } 

在这个代码示例中,我显然必须计划垃圾收集器回收新object的可能性; 因此if语句。

(请注意,我使用weakRef的唯一目的是检查新object是否仍然存在。)


 void Case_2() { var unusedLocalVar = new object(); var weakRef = new WeakReference(unusedLocalVar); GC.Collect(); // <-- doesn't have to be an explicit call; just assume that // garbage collection would occur at this point. Debug.Assert(weakRef.IsAlive); } 

此代码示例中的前一个主要更改是新的object由局部变量( unusedLocalVar )强引用。 但是,在创建弱引用( weakRef )之后,永远不会再次使用此变量。


问题:如果一个C#编译器看到unusedLocalVar仅在一个地方使用,即作为WeakReference构造函数的参数,是否允许将Case_2的前两行优化为Case_2那两行? 即, Case_2中的断言是否有可能失败?

C#编译器的作用并不重要 – 一旦它们在方法体中不再存在,JITter / GC就可以清理它们。 查看GC.KeepAlive的文档

此外,这个powerpoint演示文稿 ,特别是从幻灯片30开始,有助于解释JIT / GC可以达到的目的。

虽然我的问题已得到解答,但我想我会在MSDN博客文章“WP7:GC何时将本地变量视为垃圾”中找到我发现的相关信息: abhinaba :

[EC] ECMA规范(ECMA 334第10.9节)[…]陈述

“例如,如果范围内的局部变量是对象的唯一现有引用,但该过程中当前执行点的任何可能的继续执行中从不引用该局部变量,则实现可能(但是不要求将对象视为不再使用。“

这说明了一切。 上述文章还说,.NET框架(至少在发布模式下)将执行预测分析并释放此类对象,而.NET Compact Framework则不会(出于性能原因)。

如果一个C#编译器看到unusedLocalVar仅在一个地方使用,即作为WeakReference构造函数的参数,是否允许将Case_2的前两行优化为Case_1的那两行?

这两个定义是等价的,因此从一个转换到另一个不是“优化”,因为它们都不是更有效。

即,Case_2中的断言是否有可能失败?

是。 生产编译器不太可能不必要地保留引用,因此它将被删除,GC不会将其视为全局根并将收集该对象。

请注意,垃圾收集器在变量和范围方面看不到您的程序。 这些高级概念早在您的代码到达垃圾收集器时就被编译掉了。 GC只能看到寄存器,线程堆栈和全局变量。