我很想知道 .NET 中的对象超出范围时会发生什么。像这样的东西:
class A
{
ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?
记忆中发生了什么?当 GC 超出范围并在堆中释放它的引用时,是否会调用 GC?
我很想知道 .NET 中的对象超出范围时会发生什么。像这样的东西:
class A
{
ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?
记忆中发生了什么?当 GC 超出范围并在堆中释放它的引用时,是否会调用 GC?
记忆中发生了什么?当 GC 超出范围并在堆中释放它的引用时,是否会调用 GC?
否 - 此时不会调用 GC。
发生的情况是该对象留在内存中,但现在“无根” - 基本上,没有任何其他对象对该对象的引用。因此,该对象现在可以进行垃圾回收了。
在未来的某个时候,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 何时决定收集是必要的。
然后应该将这两个概念分开并分别进行推理。作用域是关于名称的,而垃圾收集只关心对象的可达性。当一个对象不再可以从已知的根之一到达时,它将被安排进行收集。
如果没有引用该对象,它最终会被 GC 收集。问题是,你无法准确预测它何时会发生。你只知道,它会发生。
一般来说,垃圾收集发生在 3 个不同的代(0、1 或 2)。何时收集这些资源取决于操作系统需要多少资源。
调用 GC.Collect() 将收集所有可用资源,但可以定义要收集哪一代资源。