代码之家  ›  专栏  ›  技术社区  ›  Don Vince

我如何追查我从哪里泄露了IDisposable对象?

c#
  •  3
  • Don Vince  · 技术社区  · 17 年前

    我最近调试了一些有点内存泄漏的代码。它是一个长时间运行的程序,作为Windows服务运行。

    如果你发现一个班 IDisposable

    它告诉您这一点的原因是,您(此对象的用户)现在负责清理这些资源。祝贺 你!

    作为一个有责任心的开发人员,您需要 .Dispose() 方法,以便释放这些非托管资源。

    这是个好人 using() 模式,以帮助在完成这些资源后清理这些资源。究竟是什么东西导致了漏水?

    9 回复  |  直到 17 年前
        1
  •  6
  •   Ishmaeel    17 年前

    不应该有任何情况下您不想调用Dispose,但是编译器不能告诉您 哪里

    假设您编写了一个工厂类,该类创建并返回一次性对象。当清理应该由调用方负责时,编译器是否应该因未调用Dispose而对您产生错误?

        2
  •  4
  •   Jeff Atwood    17 年前

    IDisposable更多的是为了利用using关键字。它并不是为了强迫你调用Dispose()——而是为了让你能够以一种巧妙、不唐突的方式调用它:

    class A : IDisposable {}
    
    /// stuff
    
    using(var a = new A()) {
      a.method1();
    }
    

        3
  •  3
  •   Szymon Rozga    17 年前

    好吧,如果一切顺利,CLR将在程序结束时调用所有对象的终结器,如果IDisposable模式得到正确实现,它将调用Dispose()方法。所以最后,一切都会被妥善清理。

    问题是,如果您有一个长时间运行的程序,那么您的一些IDiposable实例很可能锁定了一些不应该锁定的资源。对于这种情况,用户代码应该在处理完对象后立即使用using块或调用Dispose(),但除了代码作者之外,任何人都无法知道这一点。

        4
  •  1
  •   Otávio Décio    17 年前

    不需要调用Dispose方法。实现IDisposable接口是一个提醒,您的类可能正在使用诸如数据库连接、文件句柄等需要关闭的资源,因此GC还不够。 using 声明。

        5
  •  1
  •   Marc Charbonneau    17 年前

    一个很好的例子是.NET2.0Ping类,它异步运行。除非它抛出异常,否则实际上直到回调方法才调用Dispose。请注意,由于Ping实现IDisposable接口的方式,这个示例有一些稍微奇怪的转换,但也继承了Dispose()(只有前者才能正常工作)。

    private void Refresh( Object sender, EventArgs args )
    {
        Ping ping = null;
    
        try
        {
            ping = new Ping();
            ping.PingCompleted += PingComplete;
            ping.SendAsync( defaultHost, null );
        }
        catch ( Exception )
        {
            ( (IDisposable)ping ).Dispose();
            this.isAlive = false;
        }
    }
    
    private void PingComplete( Object sender, PingCompletedEventArgs args )
    {
        this.isAlive = ( args.Error == null && args.Reply.Status == IPStatus.Success );
        ( (IDisposable)sender ).Dispose();
    }
    
        6
  •  1
  •   STW    17 年前

    有一些工具可以帮助跟踪这些,方法是对托管堆和堆栈进行快照,并允许您查看在给定的时间点上使用了哪些对象。免费赠品是windbg使用sos.dll;需要在google上搜索一些教程,才能向您显示所需的命令,但它是有效的,而且是免费的。一个更加用户友好(不要把它与“简单”混淆)的选项是redgate的ANTS Profiler,它运行在内存分析模式下——这是一个非常灵活的工具。

    Edit:关于调用Dispose的有用性,它提供了一种确定的方法来清理对象。垃圾回收只在应用程序的分配内存用完时运行——这是一项昂贵的任务,它基本上会停止应用程序的执行,并查看现有的所有对象,然后生成“可访问”(正在使用)对象的树,然后清理不可访问的对象。在GC必须运行之前,手动清理对象将释放它。

        7
  •  0
  •   tvanfosson    17 年前

        8
  •  0
  •   Norman Ramsey    17 年前

    Dispose

        9
  •  0
  •   jrista    17 年前

    老实说,我不确定是否存在能够检查是否调用了.Dispose()的静态分析器。即使是像现在这样的静态分析,也可能有点过于主观。但是,如果您需要一个开始查找的地方,“静态C#分析”可能是最好的地方。