我对对象的垃圾收集过程感到困惑。
object A = new object();
object B = A;
B.Dispose();
通过仅对变量B调用Dispose,不会对创建的对象进行垃圾回收 因为该对象仍然被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的范围完成吗?
有没有办法处理从静态函数传递的引用?
答案 0 :(得分:16)
Dispose
不垃圾收集。您无法显式地垃圾收集特定对象。您可以调用垃圾收集器运行的请求的GC.Collect()
,但这不一样。调用Dispose
甚至不会将对象与特定变量“断开”,实际上......当该变量保持活动状态时(直到JIT最后一次检测到它将再次被读取)它将防止对象被垃圾收集。
在不再引用任何对象之前,不会对对象进行垃圾回收。不可否认,这可能比你在某些极端情况下想象的要早,但你很少需要担心这些。
值得注意的是Dispose
和垃圾收集是非常不同的事情。您致电Dispose
以释放非托管资源(网络连接等)。垃圾收集仅用于释放内存。不可否认,垃圾收集可以通过最终确定,这可能会释放非托管资源作为最后的手段,但大多数时候你应该明确处理非托管资源。
答案 1 :(得分:15)
我可能会离开,但你似乎对Dispose
和垃圾收集有误解。一旦所有对它的引用都以不确定的方式消失,对象将被垃圾收集。 Dispose通常会摆脱非托管资源,因此该对象已准备好进行垃圾回收。在你的第一个例子中,你处理了对象,理论上使它无法使用,但它仍然存在于堆上,你仍然有一个对它的引用,A和B.一旦那些超出范围,垃圾收集器可能会回收那个内存, 但不总是。在示例2中,将位图A放置在堆上,然后返回它的引用,并将B设置为该引用。然后你处理它,B超出范围。此时不再存在对它的引用,它将在以后进行垃圾收集。
答案 2 :(得分:5)
恰好Raymond Chen撰写了一系列描述.NET垃圾收集方面的博客文章。 This post最直接与您的问题有关(什么时候垃圾收集对象?)。
答案 3 :(得分:3)
Dispose()与垃圾收集没有任何关系。它所做的就是允许确定性地释放资源,但你必须明确地调用它。调用Dispose()时,调用它的对象不会收集垃圾。当所有对它的引用都消失时,它将有资格进行垃圾收集。
答案 4 :(得分:2)
这里有许多好的答案,但我也想指出人们认为你需要IDisposable的原因是GC应该真正命名为MemoryCollector甚至是ManagedMemoryCollector。在收集非托管内存资源(如文件,数据库连接,事务,窗口句柄等)时,GC并不是特别聪明。
其中一个原因是托管对象可能有一个非托管资源需要几次ram,但对GC来说它看起来像是8个字节左右。
对于文件,db conns等,您通常希望尽快关闭它们以释放非托管资源并避免锁定问题。
使用Windows句柄我们有线程亲和力来担心。由于GC在专用线程中运行,因此线程始终是用于释放窗口句柄的错误线程。
因此GC有助于避免泄漏托管内存并减少代码混乱,但仍应尽快释放非托管资源。
使用()语句是一种祝福。
PS。我经常实现IDisposable,即使我没有任何直接的非托管资源,但是它的importnt虽然通知所有实现IDisposable的成员变量,但是已经调用了Dispose。
答案 5 :(得分:1)
好的初学者Dispose!= Garbage Collected。您可以调用dispose并且永远不会收集垃圾,因为“Disposed Object”仍然可以引用它。 dispose方法用于在CG运行之前“整理”对象(关闭打开的数据库连接或文件连接等)。
object A = new object();
object B = A;
B.Dispose();
在这个例子中,B.Dispose在A上调用dispose方法,因为B正在引用变量A中的对象。它们都不是CGd,因为它们还没有超出范围。
public static image Test1()
{
Bitmap A = new Bitmap();
return A;
}
这里发生的是你正在创建对象A并返回它,所以当你离开Test1时,A最有可能被调用方法中的另一个变量引用。这意味着即使你已经离开了方法,A仍然是root(最有可能),并且在调用方法完成之前不会被CG。
public void TestB()
{
Bitmap B = Test1();
B.Dispose();
}
这里正在创建B并调用dispose。这并不意味着它将被收集。程序离开方法后,B超出范围意味着可以在下次调用GC时收集它。
答案 6 :(得分:0)
值得注意的是,调用Dispose实际上可能什么都不做。它为对象提供了清理资源(如数据库连接和非托管资源)的机会。如果您的对象包含非托管资源(如数据库连接),Dispose将告诉对象是时候清理这些引用。
垃圾收集的基本问题是,“可以达到这个目标吗?”只要堆栈上的对象具有对象的引用(或者在对象层次结构中的某处对该对象有引用),该对象就不会被垃圾回收。
示例:
ObjA创建一个ObjB,它创建一个ObjC。在ObjB不再引用Obj C之前,或者ObjA不再引用ObjB,或者直到没有对象保留对ObjA的引用之前,Obj C不会被垃圾收集。
同样,要问的问题是,“此对象当前是否可以被代码中的任何内容引用?”