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

时间:2010-04-30 07:02:43

标签: c# .net garbage-collection weak-references compiler-optimization

  

另请参阅以下相关资源:

     

换句话说:

  

本地引用的对象是否可以   变量在回收之前回收   变量超出范围(例如。   因为变量已分配,但是   然后不再使用),或者是那样   对象保证不符合条件   垃圾收集直到变量   超出范围?

让我解释一下:


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) ...
}

在这个代码示例中,我显然必须计划垃圾收集器回收new'ed 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#编译器发现Case_2仅使用了Case_1,那么它是否允许unusedLocalVar的前两行优化在一个地方,即作为WeakReference构造函数的参数?即Case_2中的断言是否有可能失败?

3 个答案:

答案 0 :(得分:11)

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

的文档

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

答案 1 :(得分:3)

虽然我的问题已得到解答,但我想我会在"WP7: When does GC Consider a Local Variable as Garbage"上发布我在MSDN博客文章abhinaba上找到的相关信息:

  

[T]他ECMA规范(ECMA 334第10.9节)[...]州

     

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

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

答案 2 :(得分:0)

  

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

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

  

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

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

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