asp.net 垃圾回收如何在对象引用上工作?

ppcbkaq5  于 2023-10-21  发布在  .NET
关注(0)|答案(7)|浏览(99)

我对对象的垃圾收集过程感到困惑。

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的作用域结束吗?
有没有什么方法可以立即处理从静态函数传递来的引用?

yqyhoc1h

yqyhoc1h1#

Dispose不进行垃圾收集。你不能显式地对一个特定的对象进行垃圾回收。你可以调用GC.Collect(),它 * 请求 * 垃圾收集器运行,但这是不一样的。调用Dispose甚至不会“断开”对象与特定变量的连接,事实上......当该变量保持活动状态时(直到JIT可以检测到它将再次被读取的最后一次),它将防止对象被垃圾收集。
一个对象不会被垃圾回收,直到它不再被任何东西引用。诚然,在某些极端情况下,这可能比你想象的要早,但你很少需要担心这些。
值得注意的是,Dispose和垃圾收集是非常不同的东西。调用Dispose释放 * 非托管 * 资源(网络连接等)。垃圾回收只是为了释放内存。诚然,垃圾回收可以通过终结,这可能会释放非托管资源作为最后的手段,但大多数时候,您应该显式地释放非托管资源。

mgdq6dx1

mgdq6dx12#

我可能已经离开了,但你似乎对Dispose和垃圾收集有误解。一个对象一旦所有的引用都消失了,就会被垃圾回收,这是一种不确定的方式。Dispose通常会清除非托管资源,因此对象已准备好进行垃圾回收。在你的第一个例子中,你Disposed了对象,理论上使它不可用,但它仍然存在于堆上,你仍然有一个对它的引用,A和B。一旦这些内存超出范围,垃圾收集器可能会回收这些内存,但并不总是如此。在例子2中,一个位图A被放在堆上,然后你返回一个它的引用,并将B设置为该引用。然后你处理掉它,B就超出了范围。在这一点上,没有更多的引用存在,它将在稍后的垃圾收集点。

u2nhd7ah

u2nhd7ah3#

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

pkbketx9

pkbketx94#

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

vhipe2zx

vhipe2zx5#

这里有很多很好的答案,但我也想指出,人们认为你需要IDisposable的原因是GC应该被命名为MemoryCollector甚至ManagedMemoryCollector。GC在收集非托管内存资源(如文件、数据库连接、事务、窗口句柄等)时并不特别智能。
原因之一是托管对象可能有一个非托管资源,它需要几个千兆字节的内存,但对GC来说,它看起来像8个字节左右。
对于文件、数据库连接等,您通常希望尽快关闭它们以释放非托管资源并避免锁定问题。
对于windows句柄,我们需要担心线程关联。由于GC在专用线程中运行,因此该线程总是释放Windows句柄的错误线程。
因此GC在避免托管内存泄漏和减少代码混乱方面有很大帮助,但仍然应该尽快释放非托管资源。
using()语句是一种祝福。
PS.我经常实现IDisposable,即使我没有任何直接的非托管资源,但重要的是要通知所有实现IDisposable的成员变量Dispose被调用。

nafvub8i

nafvub8i6#

好的,开始吧,处理!=垃圾收集。你可以调用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仍然是根的(很有可能),并且在调用方法完成之前不会被CG。

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

这里正在创建B并调用dispose。这并不意味着它将被收集。一旦程序离开该方法,B福尔斯就脱离了范围,这意味着下次调用GC时可以收集它。
When to use Dispose

nszi6y05

nszi6y057#

值得注意的是,调用Dispose实际上可能什么也不做。它使对象有机会清理资源,如数据库连接和非托管资源。如果您有一个包含非托管资源(如数据库连接)的对象,Dispose将告诉该对象是时候清理这些引用了。
垃圾收集中的基本问题是:“这个对象能达到吗?“只要堆栈上有一个对象引用了你的对象(或者在对象层次结构中的某个地方有一个对这个对象的引用),这个对象就不会被垃圾收集。
范例:
ObjA创建一个ObjB,ObjB创建一个ObjC。对象C不会被垃圾回收,直到它不再被对象B引用,或者直到对象B不再被对象A引用,或者直到没有对象保留对对象A的引用。
同样,要问的问题是,“这个对象当前可以被代码中的任何东西引用吗?”

相关问题