【问题标题】:In the Dispose(bool) method implementation, Shouldn't one set members to null?在 Dispose(bool) 方法实现中,不应该将成员设置为空吗?
【发布时间】:2010-10-02 04:13:38
【问题描述】:

讨论IDisposable 模式的指南/注释/文章都没有建议在Dispose(bool) 方法中将内部成员设置为null(尤其是当它们是占用内存的野兽时)。

在调试内部基准测试工具时,我开始意识到它的重要性。过去发生的事情是,这个缓冲区里面包含一个大数组。我们曾经为整个基准测试程序使用静态缓冲区。一旦我们完成了缓冲区,我们就无法释放这个内部数组,我们也不能让这个缓冲区可释放(因为它是静态的)。

所以,我相信,在调用Dispose() 之后,该类应该尽其所能释放它正在使用的所有资源并使它们再次可用,即使被处置的对象本身没有被回收通过 GC,并且不将成员设置为 null,因此,不允许 GC 收集内部对象意味着 Dispose 实现并不完美。

您对此有何看法?

【问题讨论】:

    标签: c# .net memory-management garbage-collection dispose


    【解决方案1】:

    Dispose 期间发布任何其他引用当然是我尝试做的事情,原因有两个:

    • 它允许对内部对象进行垃圾回收,即使已处置的对象仍在范围内
    • 如果内部对象是一次性的,这意味着即使在外部对象上重复调用Dispose(),我们也只会处理一次

    例如,我倾向于使用以下内容:

    if(someDisposableObject != null)
    {
        someDisposableObject.Dispose();
        someDisposableObject = null;
    }
    (for non-disposable, just set to null)
    someNonDisposableObject = null; // etc
    

    您可能还想将任何事件设置为 null:

    someEventHandler = null;
    

    如果调用者目前无法完全释放他们的引用(或只是忘记),这有助于将影响降至最低。虽然您应该尝试释放外部对象(用于 GC),但相对容易意外延长对象的寿命,例如通过捕获的变量(匿名方法/lambda)、事件等。

    如果你有一个终结器,那么在 GC 过程中这样做没有任何好处,你不应该真正调用外部对象上的方法(即使是 Dispose())——所以简而言之:不要这样做在 GC 扫描期间。

    【讨论】:

    • 马克,你必须总是将事件设置为空!!!在某些情况下,这可能会导致内存泄漏。赞成:很好的答案。
    • “某些情况”可能与使机器人坠毁的臭名昭著的“静态事件”问题有关。只要对象最终符合垃圾回收条件,事件通常不会直接导致内存泄漏。但是,永远不会收集静态事件 - 如果可能,尽量避免它们;-p
    • 当然,静态事件不绑定到 IDisposable(这是一个实例概念)。因此,虽然我同意将事件设置为 null,但说它会导致内存泄漏并不完全正确……不过,最好尽早释放。
    • 你能澄清你最后一句话的意思吗?仅仅是在 Dispose(false) 情况下(从终结器调用)“不要触摸托管对象”吗?
    • 确实,不要触摸终结器中的其他托管对象,因为您不知道它们是否已经被收集。而且没有必要清除字段,因为无论如何您的对象都将被删除。
    【解决方案2】:

    也许我错过了你的观点,但是一旦你的对象被释放,它相对于它的成员所代表的根或“子根”就被分离了。似乎您正在考虑像引用计数系统一样的垃圾收集(可以这样做,但......通常不是)。

    相反,可以将其视为一棵多根树,其中每个对象都有其链接到的分支。归根结底,“最终根”是静态变量以及从“主”循环中实例化的任何内容。

    当垃圾收集器运行时,考虑它所做的最简单的方法是考虑它将遍历“真正的根”列表并将“颜色”应用于它可以“到达”的所有内容。

    现在,假设收集器可以访问“所有内容”,无论它是否已植根。没有颜色的东西都可以清理干净。

    回到您最初的问题,当您的对象被处置时,人们会假设(或至少希望)没有人再引用它。如果是这种情况,它就不再有根,因此它不会有助于“着色”它所接触的任何东西。

    长话短说 - 如果在 Dispose 例程中清除成员正在修复某些问题 - 我会是你有一个不同的,真实的问题,有人持有指向你的已处置对象的链接,并在它时保持它“可访问”不应该。

    对于我写过的可能是被过度引用的信息,我深表歉意,但我有点滥用标准条款。

    【讨论】:

    • 我认为这可能是为了尽量减少释放这些对象所需的扫描量。记住,GC 是按照流程运行的:free coloured, color objects。
    • @Joe You 写道:“当您的对象被处置时,人们会假设(或至少希望)没有人再引用它”处置并不能保证对象仍然没有被持有。也许一个对象集合在使用后被释放以释放资源,但仍保留在集合中以供以后的进程删除它们?在 Dispose() 中使用 resource = null 释放大量资源不是很好的防御性编程吗?您无法知道哪些编码错误或不良做法可能会影响对象。因此,resource = null 似乎是处理大量资源的好习惯,不是吗?
    【解决方案3】:

    嗯,一般来说,这不会有什么不同。唯一会产生影响的地方是当您引用大对象堆上的对象时,您已经看到了该对象的行为。

    有一篇关于 LOH 的好文章在这里更详细地介绍了这一点:

    http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

    【讨论】:

    • 每当对已处置对象的引用未释放时都会有所不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多