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

本文关键字:条件 对象 EXACTLY 一个 | 更新日期: 2023-09-27 18:20:26

所以我知道这里的基本知识-当根不再可以访问对象时(即来自堆栈帧中局部变量的强引用或静态引用)

我的问题是关于这种潜在的优化,即使对象是从局部变量引用的,它也可能在函数中不再引用变量的任何点被垃圾收集。首先,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...
    }
}

因此,最终,现有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)

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

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

这里不可能给出一个一般的答案,因为什么时候真正符合GC条件完全取决于运行时的实现。

你唯一可以信任的是保证——也就是说,只要对象是从堆栈中引用的,它就不会被收集。

然而,在静态编译器和抖动中,当局部变量从堆栈中删除时,您无法从代码中判断出来,这很容易导致编译器优化。

因此,无论现在的确切答案是什么,在运行时的下一次小更新之后都可能不再是了——通常最好编写不依赖于这些细微之处的代码,这些细微之处只能通过实验来发现,而只能依赖于运行时的保证。

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

您提到了通过更快地收集对象来进行潜在优化的想法
我不认为这是一个优化。

如果有更多的可用内存,这并不是说计算机运行得更快或更好

分配成功或失败
如果成功,则进程运行良好
如果它失败了,进程就有麻烦了
(从可以恢复的琐碎故障到导致进程终止的深度故障)

我只是不认为像你所描述的那样对G.C.更加咄咄逼人有什么意义。这只会在边界情况下有所帮助,在这种情况下,您已经在分配99.99%的内存的情况下运行。在这种情况下,您将深入虚拟内存,并以一种疯狂的方式分页到磁盘。