我很想知道当.NET中的对象超出范围时会发生什么。像这样:
class A
{
ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?
记忆中会发生什么?当GC超出范围并且在堆中放松它时,是否会调用它?
答案 0 :(得分:3)
记忆中会发生什么?当GC超出范围并且在堆中放松它时,是否会调用它?
不 - 此时不会调用GC。
发生的事情是对象留在内存中,但现在是“无根” - 基本上,没有任何其他对象对该对象的引用。因此,该对象现在符合条件用于垃圾收集。
在未来的某个时刻,GC将会运行。发生这种情况时,无根对象现在可以进行垃圾回收。根据保持物体的哪一代,可以在那时清洁或不清洁。最后,如果程序继续执行,并且如果内存压力导致收集适当的生成,则将回收该对象的内存。
没有明确的时间点会发生这种情况(除非你明确地调用GC.Collect
,这是一个坏主意)。但是,使用托管代码,您不必担心何时会发生这种情况。假设它将在适合您的特定应用时发生。
答案 1 :(得分: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);
}
答案 2 :(得分:2)
我发现“超出范围”是一种更具C ++特定的思维方式,在给定范围的末尾,自由存储的对象被释放并且其析构函数被调用。
在C#世界中,没有“超出范围”。变量(读取:名称)存在于某个范围内,就是这样。 GC真的不关心这个问题;一个对象可以在其名称的范围结束之前收集甚至退出,或者很久之后取决于它的引用以及GC何时决定收集是否必要。
然后应该将这两个概念分开并单独推理。范围是关于名称,而垃圾收集只关心对象的可达性。当一个对象不再可以从其中一个已知的根访问时,它将被安排进行收集。
答案 3 :(得分:1)
如果没有引用该对象,它最终会被GC收集。问题是,你无法预测它究竟会发生什么。你知道,它会发生。
答案 4 :(得分:1)
一般来说,垃圾收集发生在3代(0,1或2)。收集其中每个时,取决于操作系统需要多少资源。
对GC.Collect()的调用将收集所有可用资源,但可以定义要收集哪一代资源。