【问题标题】:Memory leak, again!内存泄漏,再次!
【发布时间】:2011-02-24 11:43:29
【问题描述】:

几周前,我遇到了与 ContextMenuStrip 相关的内存泄漏问题。那个问题是固定的。 See that question here

现在我在使用 ToolStrip 控件时遇到了类似的问题。 与上一个问题一样,我正在创建大量用户控件并将它们添加到 FlowLayoutPanel。每个控件在其构造函数中为自己创建一个 ToolStrip。当从 FlowLayoutPanel 中删除一个控件(对该控件的唯一引用)时,似乎没有释放 ToolStrip 的内存。

但是,当我注释掉创建 ToolStrip 的代码时,不会发生内存泄漏。

这是否与上一个问题相同 - 我需要将 ToolStrip 设置为 null?我不明白这是怎么回事,因为这次控件正在创建条带本身,并且所有按钮事件等都在其中处理。那么当不再引用控件时,不应该对所有内容进行 GC 处理吗?

编辑: 至于 cmets,我不明白的是,我最初是用面板和一些标签“制作”我自己的工具条。标签被用作按钮。这种方式没有发生内存泄漏。

我唯一改变的是使用适当的 ToolStrip 和适当的按钮代替面板,但所有事件处理程序的连接方式相同。那么为什么它现在会泄漏内存呢?

编辑2: 我正要发布我的代码,但重新阅读了 Dave 链接到的问题。原来是 ToolStrip 的 UserPreferenceChangedEvent 问题。如果我将 ToolStrip.Visible 属性设置为 false,则不会发生内存泄漏!

现在,我可以在 Dispose 方法中执行此操作吗?如果有怎么办?我尝试复制一些代码,但收到编译警告:“MyToolStrip.Dispose()”隐藏了继承的成员“System.ComponentModel.Component.Dispose()” 我只是不明白 IDisposable 接口。

【问题讨论】:

  • 95% 的时间,您在注册事件处理程序,而不是在清除控件集合时取消注册它们。那将是我看的第一个地方。
  • 不确定这个问题是否有帮助,但我觉得它很有趣:stackoverflow.com/questions/620733/memory-leak-in-c

标签: c# memory-leaks


【解决方案1】:

95% 的情况下,您是在注册事件处理程序,而不是在清除控件集合时取消注册它们。那将是我看的第一个地方

(我认为 Juliet 的评论值得回答)

【讨论】:

  • 我刚刚添加了代码以确保在从 FlowLayoutPanel 中删除控件之前已取消订阅所有事件处理程序,并且内存使用量仍在上升。不幸的是,我的 Memory Profiler 试用版已过期。我只是不知道发生了什么事。我还没有尝试使用 IDisposable 的知识(让我读到它吓坏了 :)。但我不明白为什么在这种情况下有必要这样做。正如我所说,它工作正常(没有内存泄漏),直到我切换到使用 ToolStrip,即使没有取消订阅事件。
【解决方案2】:

正式在 C# 中不存在内存泄漏。内存在没有人使用后会被释放一段时间。

但在某些情况下,这为时已晚。特别是如果您的对象正在使用稀缺资源,您可能希望对象更早地“销毁”。

每当您看到一个类实现了 System.IDisposable 时,该类的设计者就认为在您不再需要该对象时立即 Dispose 是明智之举。这样,资源在垃圾收集器执行之前就被释放了。

如果您不 Dispose 对象,垃圾收集器会为您完成...最终。但如果您想提前释放资源,请注意在不再需要该对象时立即调用 Dispose。

您在谈论用户控件。 U用户控件是一个Control和Controls实现System.IDisposable。所以你应该调用 dispose。如果不这样做,则需要一些时间才能释放资源。

确保对象在不需要时立即释放的最简单方法是 using 语句:

using (var myFile = File.Create(...))
{
    myFile.Write(...)
    ...
}

myFile 已正确刷新/关闭/处置/最终确定,即使您有异常或出于任何原因退出 using 块:返回/中断等。

实现 System.IDisposable 通常使用模式来完成。此模式包括创建一个额外的 Dispose(bool) 函数,该函数由函数 Dispose 和析构函数调用。 bool 参数告诉您是否正在处理。

class TextWriter : System.IDisposable
{
    private StreamWriter writer = null;

    public TextWriter(string fileName)
    {
        this.writer = StreamWriter(fileName);
    }

    ~TextWriter() // destructor
    {
        this.Dispose(false); // false: I am not disposing
    }

    public void Dispose()
    {
        this.Dispose(true); // true: I am disposing
        GC.SuppressFinalize(this);
        // tell the garbage collector that this object doesn't need to be
        // finalized (destructed) anymore
     }

     private void Dispose(bool dispose)
     {
         if (this.writer != null)
         {
             this.writer.Dispose();
             this.writer = null;
         }
     }

     ...
 }

就我个人而言,我从来不需要使用布尔处理。官方说:

true 释放托管和非托管资源; false 仅释放非托管资源。

但我想不出为什么我不想释放托管资源。

如果您收到警告说您的 Dispose() 隐藏了另一个 Dispose(),那么您可能是从实现 System.IDisposable 的某些东西继承而来的。在这种情况下,你不需要析构函数和 Dispose(),你只需要 Dispose(bool)。在 MSDN 中查找它,您会发现可以覆盖它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 2011-10-08
    • 2013-01-20
    • 2011-10-31
    • 2019-08-10
    • 2013-06-24
    • 2011-03-22
    相关资源
    最近更新 更多