0

我很想知道 .NET 中的对象超出范围时会发生什么。像这样的东西:

class A
{
    ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?

记忆中发生了什么?当 GC 超出范围并在堆中释放它的引用时,是否会调用 GC?

4

5 回答 5

3

记忆中发生了什么?当 GC 超出范围并在堆中释放它的引用时,是否会调用 GC?

否 - 此时不会调用 GC。

发生的情况是该对象留在内存中,但现在“无根” - 基本上,没有任何其他对象对该对象的引用。因此,该对象现在可以进行垃圾回收了。

在未来的某个时候,GC 将运行。发生这种情况时,无根对象现在将有资格进行垃圾收集。根据持有对象的世代,它可能会或可能不会在那时被清理。最终,如果程序继续执行并且如果内存压力导致适当的代被收集,则该对象的内存将被回收。

没有明确的时间点会发生这种情况(除非您明确调用GC.Collect,这是一个坏主意)。但是,使用托管代码,您不必担心何时会发生这种情况。只需假设它会在适合您的特定应用程序时发生。

于 2012-05-02T23:19:11.697 回答
3

根据 pst 的评论,一个更好的例子可能是这样的:

void M() 
{ 
    ClassB myObjectB = new ClassB(); 
} 
//what happens to object at this point here? 

在这个例子中,myObjectB是一个局部变量而不是一个字段。那么,当局部变量超出范围时会发生什么? 没有!作用域与 C# 中的对象生命周期无关。

真正发生的是 JIT 编译器决定在某个时候释放该对象。如果变量不会在方法的其余部分中使用, 那可以在变量的范围结束之前。一旦不再引用该对象,正如其他答案也提到的那样,它就有资格被 GC 收集。直到 GC 运行(实际上,直到 GC 收集对象所在的代)才真正收集它。

正如 pst 所暗示的,一个字段是一个糟糕的例子,因为只要它的包含对象是可访问的,它总是可访问的,因此范围和对象生命周期之间的分离更大:

class A
{
    private object o = //whatever
}

void B()
{
    var a = new A();
    // here, the field o is not in scope, but the object it refers to is reachable and cannot be collected.
    GC.KeepAlive(a);
}
于 2012-05-02T23:22:30.250 回答
2

我发现“超出范围”是一种更特定于 C++ 的思考方式,在给定范围的末尾,具有自动存储的对象被释放并调用其析构函数。

在 C# 世界中,没有“超出范围”。变量(读取:名称)存在于某个范围内,仅此而已。GC 真的不关心这个;一个对象可以在其名称范围结束之前甚至退出之前被收集,也可以在很久之后被收集,具体取决于引用它的内容以及 GC 何时决定收集是必要的。

然后应该将这两个概念分开并分别进行推理。作用域是关于名称的,而垃圾收集只关心对象的可达性。当一个对象不再可以从已知的根之一到达时,它将被安排进行收集。

于 2012-05-02T23:20:08.627 回答
1

如果没有引用该对象,它最终会被 GC 收集。问题是,你无法准确预测它何时会发生。你只知道,它会发生。

于 2012-05-02T23:12:25.620 回答
1

一般来说,垃圾收集发生在 3 个不同的代(0、1 或 2)。何时收集这些资源取决于操作系统需要多少资源。

调用 GC.Collect() 将收集所有可用资源,但可以定义要收集哪一代资源。

于 2012-05-02T23:33:49.590 回答