当EXACTLY是一个符合C#垃圾收集条件的对象时?

Kev*_*vin 8 c# garbage-collection

所以我知道这里的基础知识 - 当一个对象不再可以被根访问时(即来自堆栈帧中的局部变量或静态引用的强引用),该对象有资格进行垃圾回收.

我的问题是这个潜在的优化,即使从局部变量引用一个对象,它也可能在函数中不再引用该变量的任何点进行垃圾收集.首先 - 似乎现有的C#实现不会这样做 - 2.0和4.0似乎都会保持本地引用"直播",直到堆栈帧被销毁.但是 - 如果在CLR的更高版本中优化垃圾收集时,我还想编写仍然健壮的代码.

所以 - 不用多说了,这里有一些代码说明:

class Foo 
{
  ...
}
class Program
{
    public static void fxn1(int blah) 
    {
      ...
    }
    public static void fxn2(Foo foo)
    {
      ...
    }
    public static int ToInt(Foo foo)
    {
      ...
    }
    public static void Main()
    {
      ...
      Foo foo = new Foo();
      fxn2(foo); // I THINK foo may not be GC'ed until fxn2 returns...
        // I THINK foo may be GC'ed here, even though CLR2.0 and CLR4.0 don't...
        //  (experiment shows CLR 2.0 and 4.0 leave foo "live" until Main returns)
      fxn2(new Foo()); // I THINK the argument can't be GC'ed until fxn2 returns...
        // I KNOW that even CLR2.0 and CLR4.0 will GC the argument after the return...
      fxn1( ToInt(new Foo()) ); // I KNOW that new Foo is GC'able even within fxn1...
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,最终,现有CLR的规则似乎是:1.任何对象在函数调用期间是"活动的",它是一个直接参数2.任何对象在函数调用期间是"活动的"它由未重新分配的本地堆栈变量引用.(即使函数末尾的几条指令可能没有引用堆栈变量)

但是 - 显然C#保留修改(2)的权利,以便在函数内最终使用引用之前,对象是"实时"的.

这意味着:

Foo foo = new Foo();
Foo foo2 = new Foo();
fxn2(foo); // foo is NOT GC'able until fxn1 returns?
   // foo IS GC'able from here on? (b/c no further uses of local "foo"?)
fxn2(foo2); // foo2 is NOT GC'able within fxn2 ?
fxn1(ToInt(foo2)); // foo2 IS GC'able within fxn1 ? (existing CLR does not GC foo2)
Run Code Online (Sandbox Code Playgroud)

ECMA规范中是否有任何内容可以详细处理垃圾收集资格?

yeo*_*man 6

好吧,这里不可能给出一般性答案,因为事情实际上符合GC的条件完全取决于运行时的实现.

唯一值得信赖的是保证 - 即,只要从堆栈中引用对象,就不会收集它.

但是,当从堆栈中删除局部变量时,您无法从代码中看出 - 这很容易在静态编译器和抖动中进行编译器优化.

因此,在运行时的下一次微小更新之后,现在可能不再是精确的答案了 - 通常最好编写不依赖于这些细微之处的代码,这些微妙之处只能通过实验找到,而是依赖于运行时的只保证.


Kev*_*vin 1

@M.Babcock - 感谢您提供 ECMA 规范的链接!8.4 实际上太笼统了,但我正在寻找的答案是在 10.9 中 - 并且与 Java 相同 - 当一个变量不再被任何可能的未来代码路径引用时,那么它被认为有资格进行垃圾收集 - 这意味着尽管现有的 clr 实现似乎将局部变量生命周期限制在堆栈中,但不能保证第三方或未来的实现会这样做。