.NET对象何时会被垃圾回收器回收,与作用域有关吗?

4

大家好,

如果我在一个长时间运行的方法中(不一定是CPU密集型...只是长时间运行)将一个大对象设置为null,它是否立即可以进行垃圾回收,或者需要等到该方法完成后该对象才准备好进行垃圾回收?


3
  1. 你不将“对象”设为null,而是将“变量”设为null的“引用”。
  2. 当一个对象不再具有“根”,它就可以被回收了。执行“foo = null;”可能不能使之前由foo引用的对象无法访问(因为可能在其他地方仍然有对它的引用)。
- Ani
1
如果引用存储在本地变量中,JIT优化器将删除该赋值。Jitter已经能够计算其生命周期,这是它需要为收集器生成的信息。 - Hans Passant
3个回答

7

这种方法不需要完全执行,但如果GC可以确定您不会再次从中读取变量,则无需将变量设置为null。例如:

public void Foo()
{
    SomeObject x = new SomeObject();
    // Code which uses x

    Console.WriteLine("Eligible for collection");

    // Code which doesn't use x.
}

在指定点,该对象有资格进行收集 - 当然,假设没有其他东西保留了对它的引用。重要的是是否会有任何东西能够再次读取该值。您甚至可以将变量分配为不同的值,然后读取它,只要垃圾回收器知道它不会再次看到原始值,那就不会作为GC根。例如:

using System;

class Bomb
{
    readonly string name;

    public Bomb(string name)
    {
        this.name = name;
    }

    ~Bomb()
    {
        Console.WriteLine(name + " - going boom!");
    }    

    public override string ToString()
    {
        return name;
    }
}

class Test
{
    static void Main()
    {
        Bomb b = new Bomb("First bomb");
        Console.WriteLine("Using bomb...");
        Console.WriteLine(b);
        Console.WriteLine("Not using it any more");

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("Creating second bomb...");
        b = new Bomb("Second bomb");
        Console.WriteLine("Using second bomb...");
        Console.WriteLine(b);
        Console.WriteLine("End of main");
    }
}

输出:

Using bomb...
First bomb
Not using it any more
First bomb - going boom!
Creating second bomb...
Using second bomb...
Second bomb
End of main
Second bomb - going boom!

实际上,情况可能更加极端:当垃圾回收器发现没有任何代码会再次读取某个对象的字段时,即使某个方法仍在该对象中运行,该对象也可以被认为是可回收的。以下是一个简短但完整的示例:
using System;

class Bomb
{
    int x = 10;

    ~Bomb()
    {
        Console.WriteLine("Boom!");
    }

    public void GoBang()
    {
        Console.WriteLine("Start of GoBang");
        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("x={0}", x);
        Console.WriteLine("No more reads of x");
        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("Returning");
    }
}

class Test
{
    static void Main()
    {
        Bomb b = new Bomb();
        b.GoBang();
        Console.WriteLine("Still in Main");
    }
}

输出:

Start of GoBang
x=10
No more reads of x
Boom!
Returning
Still in Main

(请不要在调试器中运行此代码,因为调试器会延迟垃圾回收以便您可以查看变量。)

需要注意的一点是:您的问题谈到了将一个“对象”设置为null……这个概念不存在。您只能将一个“变量”设置为null。值得区分这两者之间的差别。


谢谢详细的解释,Jon!你是对的... 我应该说变量,没有将对象设置为null的概念。 - Suraj

7

@SFun:没关系。嘿,我得到了一个回答,一度比Jon Skeet的赞数还多。这已经很不错了 ;-)。说真的,他对此的了解比我多得多,应该得到采纳的答案。我所做的只是链接到一篇基本上解释了相同内容的文章。 - Joey

1

假设之前的值不再被任何东西引用,它将立即符合垃圾回收的条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接