2012-05-02 48 views
0

我很想知道当.NET中的对象超出范围时会发生什么。类似这样的:局部范围后的'对象'会发生什么?

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

在内存中会发生什么? GC在超出范围并在堆中放弃参考时会被调用吗?

+3

GC将对象“在不再可达到的某个点之后”收回。然而,这是一个不好的例子,因为'myObjectB'只在一个'new A'(永远不会发生)时才被赋值,并且“a”将保持由myObjectB命名的对象强烈可达,只要“a”本身是强烈可及的。 – 2012-05-02 23:10:43

+0

“如果某个线程无法遍历任何[soft/weak/phantom]引用对象[可以从根]到达它,那么该对象是*强烈可达*。” (这里是[Java中的位](http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html),但它也适用于C#,减去一些参考“强度”水平。) – 2012-05-02 23:20:07

+0

这段代码甚至不编译。 – Aliostad

回答

3

内存中会发生什么? GC在超出范围并在堆中放弃参考时会被调用吗?

否 - 此时GC不会被调用。

会发生什么事是对象留在内存中,但现在是“无根” - 基本上,没有任何其他对象引用该对象。因此,该对象现在是合格垃圾收集。

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

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

+0

在Java中,这些术语是强烈可及的(并且无法访问):在谈论C#/ CLR时,您是否知道使用任何此类“权威”术语? – 2012-05-02 23:21:48

+0

@pst我相信该规范使用“扎根”,这就是为什么我使用该术语。这是对象的“正常”句柄[vs.一个弱或固定的句柄,在上面的示例中](请参阅GCHandle:http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle.aspx) –

+0

@pst C#语言规范使用“未使用对象“(第1部分,简介)引用无根对象....现在查找CLI规范术语。 –

1

如果没有引用该对象,它最终会被GC收集。事情是,你无法预测到底什么时候会发生。你只知道,它会发生。

+0

但我的答案是什么时候? –

+0

@Raj,这正是问题所在。你不能确定与c#。这不是C++,您可以在其中控制应用程序的每一个位置。如果你需要这种行为,也许C#不适合你。其他人用更多的话来解释它,但原理仍然是一样的。可能对你的问题最准确的答案是:“最终”。 – walther

+0

明白了,谢谢! –

2

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

在C#世界中,没有“超出范围”。变量(读取:名称)存在于某个范围内,就是这样。 GC真的不担心这一点;一个对象可以在其名称范围的结尾甚至退出之前收集,或者之后很长时间,这取决于它是什么引用以及GC何时决定收集是必要的。

这两个概念应该分开离婚和推理。范围界定关于名称,而垃圾收集只关心对象的可达性。当一个对象不再可以从其中一个已知的根中访问时,它将被计划收集。

+0

嗯,是且不是 - (在这里删除方程中的优化)在上面的例子中,该对象在引用超出范围之前(或被设置为引用不同的对象或null)不能变为无根。我认为说这两个概念完全分离是不公平的 - 但它与C++的思想意义完全不同。 –

+0

@ReedCopsey但你为什么要从等式中去掉优化?大部分代码都经过优化 – phoog

+0

由于在没有进一步限定的情况下,它的名字被限制在那里,因此该参考文件在课程以外的任何地方都是“超出范围”。我认为你将生命周期与范围混为一谈,但我相信C#在两者之间做了一定的区分。请参阅:“•由类成员声明(第10.2节)声明的成员的范围是声明发生的类主体。另外,类成员的范围扩展到派生类的范围包含在成员的可访问性域(第3.5.2节)中的类“。 – MikeP

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); 
} 
1

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

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