调用Dispose()函数后未释放对象

时间:2010-05-07 05:58:40

标签: c# dispose

我有关于处理对象的疑问。方案如下。

在使用C#开发的桌面应用程序中,我有一个函数,其中创建的对象就像这样。

namespace Class 1
{
    variables section;
    ....
    ....

    Function1()
    {
         local variables;
         try
         {
              Object1 obj = new Object1();
              ....
              ....
              if(true)
              {
                  ....
              }
              else
              {
                   **obj.Dispose();**
              }
          }
          catch()
          {}
          finally
          {}
     }
}

执行else部分时不会释放对象。 msdn链接是

http://msdn.microsoft.com/en-us/library/system.componentmodel.component.dispose(VS.90).aspx

根据该组件应该重新使用它所使用的所有资源。

我想知道,为什么不处理这个物体。

谢谢。

帕瓦纳瓦利。

6 个答案:

答案 0 :(得分:4)

由于您的自定义对象似乎继承自System.ComponentModel.Component,因此您需要覆盖Dispose(bool)以处置对象的资源。尽管您的对象继承自System.ComponentModel.Component(具有Dispose方法),但除非您对dispose方法进行编码,否则不会丢弃对象的自定义资源。 (这不是通过继承System.ComponentModel.Component而获得的自动内容。只有System.ComponentModel.Component基类已知的资源才会被处理掉。)

在课堂上尝试这样的事情:

protected override void Dispose(bool disposing)
{
    // release your resources here
}

我们不知道您的对象上的哪些资源没有被处理掉,但无论对象如何,必须对dispose方法进行编码以便释放资源。如果在处置方法中没有处置对象的资源,则在处置它时不会处置它。

例如:

ExpensiveResource myExpensiveResource1;
ExpensiveResource myExpensiveResource2;

void Dispose()
{
    // release the resources
    myExpensiveResource1.Dispose();

    // since there is no call to dispose of myExpensiveResource2, it is not disposed of
}

答案 1 :(得分:3)

IDisposable.Dispose只是一个类可以选择实现的方法。该方法的内容完全由类设计者决定。为了准确理解出现了什么问题,在处理/最终确定对象时应该理解许多术语。

处置

处理对象意味着只需调用对象的Dispose方法。对象是从该函数释放其非托管资源(文件,网络句柄,流)的惯例。如果在调用Dispose后保留对象的引用,则除非您尝试访问已在Dispose方法中释放的资源,否则该对象仍将主要起作用。

结束写入

当.NET CLR Finalizer确定没有更多对象使用此特定对象时,最终确定一个对象,并且现在可以回收它占用的内存。程序员无法直接控制强制运行时声明对象“死”。您可以强制垃圾收集器运行,但即使这样也不建议(即您真​​的需要知道您正在做什么才能使用GC)。

我怎么知道Dispose已经运行了?

嗯,最好的方法是在Dispose方法中设置断点。如果您还没有源代码,可以使用this method

我是否可以通过查看进程消耗的内存量来判断Dispose是否已运行?

没有。运行Dispose后,CLR对象仍然存在。对象完成后,内存将释放到托管内存池。如果你确切地知道它是什么类型的资源,你可以使用第三方操作系统工具,例如Process Explorer来查看该进程是否发布了文件句柄。即使这样,释放句柄的过程与认为它被释放的操作系统之间可能存在延迟。

我怎么知道对象已经完成?

再次,通过在终结器中设置断点或运行内存分析器。即使终结器运行,很可能(实际上肯定是)内存不会释放到操作系统。它保留在托管内存池中,CLR运行时可以选择以后使用它。

即便如此,当内存释放到操作系统时,该过程看起来就像使用该内存一样。当感觉需要它时,操作系统需要记忆。

如果你试着通过查看它在Process Manager中消耗的内存量来弄清楚你的应用程序在做什么,我可以保证你会疯狂地尝试分析。事实上,尝试这样做与尝试使用regular expressions to parse HTML相同。

结论

这些都没有真正回答你的问题。所以,这里有一个清单来找出你的问题,因为我们没有太多可以继续下去:

  1. 你怎么知道Dispose没有运行?您是否已调试应用程序并在此行上设置断点:**obj.Dispose();**
  2. 如果断点被击中,您是否已逐步完成代码以弄清楚发生了什么?
  3. 您能否澄清对象未被处置的含义?

答案 2 :(得分:2)

如果没有看到你的代码,很难肯定地说,但是你可能会错误地将'Dispose'误用为c ++中的析构函数或delete / free

答案 3 :(得分:2)

你正在混淆处理的概念,在.NET中意味着“IDisposable的实现,因此是Dispose ()方法”,并保证非托管资源被释放,以及垃圾收集的概念,它由运行时处理并意味着“释放分配给对象的内存”。当您维护对任何对象的引用时,垃圾收集器不会触及它 - 因此在发生这种情况之前,不释放该对象分配的内存。

这些容易混淆的概念有两个原因:

首先,通过using语句处理IDisposable个对象的一般做法如下:

using (IDisposable d = new MyDisposableObject ()) {
    // do whatever you need d for
}

确保在退出d块时处理using;但它也限制了d到块的范围,这意味着一旦执行存在块,该对象也可以自由地进行垃圾收集(不一定是立即执行,但这既不是在这里也不是他们自己的代码不再维护对它的引用)。所以在这个(非常普遍的,应该被认为是标准的)成语中,处理在消除参考的同时发生。

其次,直接访问非托管资源的类的良好实践是实现调用Dispose ()的终结器。当对象被垃圾收集时终结器运行,因此您可以确保不会在非托管资源上留下悬空泄漏,但如果对象从未被正确处理掉,那么您将决定何时释放非托管资源严格掌握在运行时的手中,运行时决定何时仅基于内存压力执行垃圾收集,并且对非托管资源的引用数量或类型不了解或不感兴趣。因此,在这种情况下,对象的垃圾收集会强制处理非托管资源。

如果您在某个方法上明确调用Dispose (),例如在您的问题中(当然假设Object1实现IDisposable),您仍然保留对{{obj的引用1}},因此可以访问它的所有属性和方法。当然,这些方法或属性需要您已经处理的非托管资源当然是完全合理的,在这种情况下可能会抛出System.ObjectDisposedException,或者Object1的作者选择处理这些资源那种情况,但如果他们不需要你已经释放的资源,那么他们按预期运作也是完全合理的。

例如,请查看System.IO.FileStream。处理完毕后,ReadByte ()等操作会抛出异常,因为显然无法访问要读取的文件,但调用属性CanRead只返回false,是有效的和预期的。 (例如,MemoryStream甚至可以在Dispose ()调用之后访问数据,因为(当前)实现使用由运行时管理的缓冲区而不是像内核句柄那样的任何东西。

答案 4 :(得分:0)

首先执行“其他部分”吗?你说“如果部分”总是如此

答案 5 :(得分:0)

您应该重构您的代码,如下所示:

Function1()
{
  local variables;
  try
  {
     using (Object1 obj = new Object1())
     {
       ....
       ....
       if(true)
       {
         ....
       }
     }
  }
  catch() // I hope you don't use try-catch like this in real code
  {}      // I hope you don't use try-catch like this in real code
  finally // I hope you don't use try-catch like this in real code
  {}      // I hope you don't use try-catch like this in real code
}

using语句确保obj在离开作用域时始终被调用。