内部作用域中的嵌套引用类型对象对垃圾收集没有影响:是或否?

mob*_*bbi 4 c# garbage-collection scope

我一直在阅读Jeffrey Richter的精美书"CLR via C#"中的垃圾收集章节.在那里,他通过引用从JIT编译器发出的本机代码的反汇编列表,说明了GC在概念上如何工作(如何标记根)的示例.从这个例子中,我发现范围内的嵌套引用类型似乎对加速嵌套变量的垃圾收集具有零效应.我想知道我是否正确理解这一点.无论如何,请考虑以下两个版本的代码:

A)在内部范围中嵌套引用类型变量(y):

namespace scope
{
    class A { public void foo() { } }
    class Program
    {
        static void Main(string[] args)
        {
            A x = new A();
            x.foo();
            {
                A y = new A();
                y.foo();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

B)与上述相同,除了x和y在相同的范围内.

namespace scope
{
    class A { public void foo() { } }
    class Program
    {
        static void Main(string[] args)
        {
            A x = new A();
            x.foo();

            A y = new A();
            y.foo();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

出于好奇,我检查了两个版本生成的IL代码,它们是相同的!

Q1:所以,这似乎意味着确实,范围确定不会以任何方式加速垃圾收集.它是否正确?(注意:我知道"使用"声明 - 但我只是对GC中"普通旧范围"的行为感到好奇,如上面的两个例子所示.)

Q2:如果Q1的答案是"真",那么我完全感到困惑的是"对象生命周期不是由范围决定"的情况可能会发生,如下所述:http: //www.curly-brace.com/ favorite.html

Jon*_*eet 8

垃圾收集部分取决于您是否在调试器中运行.我假设我们不是.

它实际上可能比你想象的要激进得多.例如:

object o = new object();
Console.WriteLine(o);   
Console.WriteLine("hi");
o = new object();
Run Code Online (Sandbox Code Playgroud)

该对象可以在第二行之后立即进行垃圾收集 - 即在变量o退出范围之前很久.没有最后一行也是如此.

换句话说,范围界定不会加速垃圾收集 - 因为GC已经比你想象的更聪明了.

实际上,垃圾收集器可能更具攻击性.考虑以下代码:

Foo f = new Foo();
f.SomeMethod();
Console.WriteLine("Hello");
Run Code Online (Sandbox Code Playgroud)

Foo看起来像这样:

public class Foo
{
    int x = 10;

    public void SomeMethod()
    {
        Console.WriteLine(x);
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("Hello");
            Thread.Sleep(100);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

理论上,垃圾收集器可以运行时收集Foo对象,SomeMethod只要它经过第一行,它实际上是从中读取的x.如果Foo有一个终结器,你可以在一个线程SomeMethod中运行终结器而在另一个线程中运行.可怕的东西.


Eri*_*ert 5

乔恩当然是正确的.对于一些额外的背景,我向您推荐C#规范,其中指出:

局部变量的实际生命周期取决于实现.例如,编译器可能静态地确定块中的局部变量仅用于该块的一小部分.使用此分析,编译器可以生成导致变量存储的生命周期比其包含块短的代码.

并且,也很重要:

由本地引用变量引用的存储被回收,而与该本地引用变量的生命周期无关.

请注意,我们正在区分被引用对象的生命周期- 即允许垃圾收集器进入的时间 - 以及保存引用的变量的生命周期.正如Jon指出的那样,如果我们可以证明这样做不会释放某人仍然可以持有的引用,那么变量和它所引用的对象的生命周期都会被编译器和垃圾收集器大大缩短.