2010-08-13 92 views
9

我对物体上的垃圾收集过程感到困惑。垃圾收集如何处理对象引用?

object A = new object(); 
object B = A;   
B.Dispose(); 

通过调用处置可变只有B,创建对象将不会被垃圾收集 为对象仍然是已经A.引用

现在做下面的代码工作上面一样?

public static image Test1() 
{ 
    Bitmap A = new Bitmap(); 
    return A; 
} 

现在我从其他方法调用这个静态函数。

public void TestB() 
{ 
    Bitmap B = Test1(); 
    B.Dispose(); 
} 

静态函数Test1返回对Bitmap对象的引用。该引用在另一个变量B中保存为 。通过调用B上的Dispose,B和对象之间的连接会丢失,但是从Test1传递的引用会发生什么情况。在功能TestB的范围完成之前它会保持活动状态吗?

有没有什么办法可以立即处理从静态函数传递来的引用?

+2

Garbage收集不是参考计数。 – 2010-08-13 17:45:45

+0

垃圾收集的重点在于,您不必关心何时或如何释放内存。原则上,在一个拥有大量内存的系统上,垃圾收集可能不会发生,仅仅因为在应用程序终止时让所有内容都被清理起来会更有效率。 – 2010-08-13 18:21:44

回答

15

我可能不在,但您似乎对Dispose和垃圾收集存在误解。一旦对象的所有引用都以非确定的方式消失,对象将被垃圾收集。 Dispose通常会摆脱非托管资源,因此该对象已准备好进行垃圾回收。在你的第一个例子中,你抛弃了这个对象,理论上它使它不可用,但它仍然存在于堆中,并且你仍然有一个对它的引用,既有A也有B对象。一旦这些对象超出了作用域,垃圾收集器就可以回收这个内存, 但不总是。在示例2中,将位图A放在堆上,然后返回它的引用,并将B设置为该引用。然后你处置它,B超出范围。那时,再也没有提到它,它将在稍后的时间收集垃圾。

+0

感谢您的回复。我应该让我的问题更清楚。我知道通过调用Dispose(),GC不会立即释放内存,但它会从可终结队列中移除对象的引用,以便该对象不会移动到下一个Gen。在第二个示例中,我们如何调用dispose关于函数在使用后立即传递的引用?如果我们不能处理静态函数传递的引用,它将移动到下一代,并且释放资源将会有更多延迟。 – kishore 2010-08-13 18:16:29

+1

@kishore:当你说处理参考时,你的意思非常混乱。至少在这种情况下,引用是指向堆上的对象的堆栈上的指针。您在堆上的实例中处置非托管资源。 – 2010-08-13 18:36:28

+0

对不起,我感到困惑。那么是否有任何方法可以删除或清除函数传递的引用,以便它不会指向堆上的对象? – kishore 2010-08-13 19:25:05

15

Dispose确实不是垃圾收集。你不能明确地垃圾收集一个特定的对象。您可以拨打GC.Collect(),其中请求垃圾收集器运行,但这不一样。调用Dispose甚至不会将对象从特定变量“断开连接”,事实上......当该变量保持有效时(直到上次JIT可以检测到它将再次被读取),它将防止对象被垃圾收集。

一个对象不会被垃圾收集,直到它不再被任何东西引用为止。无可否认,这可能比您在某些极端情况下可能会想到的要早,但您很少需要担心这些。

值得注意的是,Dispose和垃圾收集是非常不同的东西。您致电Dispose发布非托管资源(网络连接等)。垃圾收集仅用于释放内存。可以肯定的是,垃圾收集可以通过终结,这可能会释放非托管资源作为最后的手段,但大多数情况下,您应该明确处置非托管资源。

+0

作为附录,你永远不应该依赖终结器,因为它们可能永远不会运行。充其量,他们可能是“最后的胜地”,如果该计划忘记明确处理这些资源,它们将尝试清理资源。 – siride 2010-08-13 19:33:59

3

Dispose()与垃圾回收没有任何关系。它所做的只是允许确定性地释放资源,但你必须明确地调用它。调用Dispose()时,您调用它的对象不会收集垃圾。当所有对它的引用都不存在时,它将有资格进行垃圾回收。

5

碰巧Raymond Chen刚刚写了一系列描述.NET垃圾收集方面的博客文章。 This post与你的问题最直接相关(什么时候垃圾收集对象?)。

+0

我期待每年的CLR周:) – 2010-08-13 17:31:53

+0

我打算发布一个链接,但我今天早上感觉有点懒。他在GC相关主题上有一些很棒的帖子。 – siride 2010-08-13 19:34:21

+0

感谢您分享这篇文章。 – kishore 2010-08-13 21:59:29

1

好的初学者Dispose!=垃圾收集。你可以调用dispose并且从来没有收集垃圾,因为“Disposed Object”仍然可以引用它。 dispose方法用于在CG运行之前“清理”对象(关闭打开的数据库连接或文件连接等)。

object A = new object(); 
object B = A;   
B.Dispose(); 

在这种情况下B.Dispose呼吁一个Dispose方法,因为B被引用对象的变量A.无论将CGD,因为他们没有脱落的范围呢。

public static image Test1() 
{ 
    Bitmap A = new Bitmap(); 
    return A; 
} 

这里发生的事情是,你正在创建一个对象并返回,所以当你离开的Test1,A是最有可能被在调用方法的另一个变量引用。这意味着即使你已经离开了这个方法,A仍然是根(很有可能),并且在调用方法完成之前不会被CG'd。

public void TestB() 
{ 
    Bitmap B = Test1(); 
    B.Dispose(); 
} 

这里B正在创建和调用dispose。这并不意味着它会被收集起来。一旦程序离开方法,B就不在范围内,这意味着它可以在下次调用GC时收集。

When to use Dispose

2

许多很好的答案在这里,但我也很喜欢这一点,人们以为你所需要的IDisposable的原因是,GC应该被命名为MemoryCollector甚至ManagedMemoryCollector。当收集非托管内存资源(如文件,数据库连接,事务,窗口句柄等)时,GC并不特别聪明。

其中一个原因是托管对象可能有一个非托管资源需要几个ram的演出,但对于GC看起来像8个字节左右。

对于文件,db conns等,您通常希望尽快关闭它们以释放非托管资源并避免锁定问题。

使用windows句柄我们有线程亲和力担心。由于GC运行在专用线程中,线程始终是释放窗口句柄的错误线程。

因此,GC可以帮助您避免泄漏托管内存并减少代码混乱,但仍然应尽快释放非托管资源。

使用()语句是一种祝福。

PS。尽管我没有任何直接的非托管资源,但通常我会实现IDisposable,但它的进口通知所有实现IDisposable Dispose被调用的成员变量。

0

值得注意的是,调用Dispose可能实际上什么也不做。它为对象提供了清理资源的机会,例如数据库连接和非托管资源。如果您有一个包含非托管资源(如数据库连接)的对象,则Dispose将告诉该对象是清理这些引用的时间。

垃圾收集的根本问题是,“可以达到这个目标吗?“只要堆栈上存在对象引用的对象(或者对象层次结构中存在对此对象的引用),该对象将不会被垃圾收集。

示例:

ObjA创建一个ObjB,它创建一个ObjC Obj C不会被垃圾收集,直到它不再被ObjB引用,或者直到ObjB不再被ObjA引用,或者直到没有保留引用的对象ObjA

同样,要问的问题是:“该对象目前是否可以被代码中的任何内容引用?”

+0

我也应该提到,仅仅因为一个对象没有被引用,并不意味着它会立即被收集。垃圾收集器根据对象活着的时间长度,它消耗多少内存以及系统中的内存压力来决定要收集的内容以及时间。 这需要更多地了解系统中的垃圾收集器,以真正优化内存使用。但是对于GC中的初学者来说,一旦无法访问对象,假设内存由垃圾收集器拥有是安全的。 – Jason 2010-08-13 19:25:58

+0

-1因为这个帖子混淆了GC和Dispose()的目的。的确,Dispose()可以清理非托管资源。但是它被确定性地和明确地由你的代码调用。它永远不会被垃圾收集器调用,调用Dispose()永远不会导致垃圾收集对象。 – siride 2010-08-13 19:36:08