本地范围之后的“对象”会发生什么

本文关键字:什么 对象 范围 之后 | 更新日期: 2023-09-27 18:33:04

我渴望知道当 .NET 中的对象超出范围时会发生什么。像这样:

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

内存中发生了什么?当 GC 超出范围并在堆中丢失引用时,是否会调用它?

本地范围之后的“对象”会发生什么

内存中发生了什么?当 GC 超出范围并在堆中丢失引用时,是否会调用它?

否 - 此时不会调用 GC

发生的情况是,该对象保留在内存中,但现在是"未root"的 - 基本上,没有从任何其他对象引用该对象。 因此,该对象现在符合垃圾回收的条件。

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

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

根据 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);
}

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

在 C# 世界中,没有"超出范围"。变量(读取:名称(存在于某个范围内,仅此而已。总理事会对此确实不关心;可以在对象名称范围结束之前收集对象,也可以在很久之后收集对象,具体取决于引用它的内容以及 GC 决定需要收集的时间。

然后,这两个概念应该分开并分别推理。范围是关于名称的,而垃圾回收只关心对象的可访问性。当无法再从某个已知根访问对象时,将计划对其进行收集。

如果没有引用

对象的内容,它最终会被 GC 收集。问题是,你无法预测它究竟什么时候会发生。你只知道,它会发生。

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

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