所以我知道这里的基础知识 - 当一个对象不再可以被根访问时(即来自堆栈框架中的局部变量或静态引用的强引用),该对象有资格进行垃圾收集。
我的问题是这个潜在的优化,即使从局部变量引用一个对象,它也可能在函数中不再引用变量的任何点进行垃圾收集。首先 - 似乎现有的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规范中有没有详细说明垃圾收集资格?
答案 0 :(得分:6)
嗯,这里不可能给出一般性答案,因为事情实际上符合GC的条件完全取决于你的运行时的实现。
唯一值得信赖的是保证 - 即,只要从堆栈中引用对象,就不会收集它。
当从堆栈中删除局部变量时,你无法从代码中看出 - 这很容易在静态编译器和抖动中进行编译器优化。
因此,在运行时的下一次小更新之后,现在可能不再是精确的答案了 - 通常最好编写不依赖于这些微妙的代码,这些代码只能通过实验找到,而是依赖于仅在运行时的保证。
答案 1 :(得分:1)
@ M.Babcock - 感谢您提供ECMA规范的链接! 8.4实际上太笼统了,但我正在寻找的答案是10.9 - 并且与Java相同 - 当一个变量不再被任何可能的未来代码路径引用时,它被认为有资格进行垃圾收集 - 这意味着虽然现有的clr实现似乎将局部变量的生命周期限制在堆栈中,但不能保证第三方或将来的实现会这样做。
答案 2 :(得分:0)
您可以通过更快地收集对象来提及潜在优化的想法 我不认为这是一种优化。
如果计算机有更多可用内存,它就不会运行得更快或更好。
分配成功或失败 如果成功,则该过程正常运行 如果失败,则该过程有问题 (任何事情,从琐碎的麻烦,它可以恢复,到深层次的麻烦,导致流程终止)
我只是没有看到更加积极的G.C.如你所描述的那样它只会在你已经运行99.99%内存的边界情况下有所帮助。在这种情况下,你会深入虚拟内存,并以疯狂的方式分页到磁盘。