Windows窗体应用程序中的内存泄漏

Kas*_*hif 23 .net c# memory-leaks winforms c#-3.0

我们正在开发一个大的.NET Windows Forms应用程序.尽管我们正在处理表单,但我们正面临内存泄漏/使用问题.

场景如下:

  1. 我们的应用程序使用60 KB的内存,并在网格中显示记录列表.
  2. 当用户点击记录打开表单时myform.showDialog,显示详细信息.内存从60 KB 跳到105 MB.
  3. 现在我们关闭表单myform以返回到网格,并处理该表单并将其设置为null.内存保持105 MB.
  4. 现在,如果我们再次执行步骤2,会跳,从105 MB150 MB等.

当我们关闭时,我们如何释放记忆myForm

我们已经尝试过GC.Collect()等,但没有任何结果.

STW*_*STW 25

寻找泄漏的第一个地方是事件处理而不是缺少Dispose()调用.假设您的容器(父窗体)加载子窗体并为该子窗体(ChildForm.CloseMe)的事件添加处理程序.

如果要从内存中清除子表单,则必须先删除此事件处理程序,然后才能将其作为垃圾回收的候选者.

  • 是的.当我们开始收到"OutOfMemoryExceptions"的报告时,我们得到了一个*吨*的WinForms代码,导致几个月的搜索结束这些类型的泄漏.肯定是一种痛苦!我仍然不高兴MS的关于`AddHandler/+ =`调用的文档没有关于需要保证`RemoveHandler/ - =`被调用的巨大闪烁警告 (4认同)
  • +1 - 打败我吧.这绝对是我在.NET中遇到的最常见的内存泄漏形式 (3认同)

Otá*_*cio 11

处理表格并不一定能保证您不会泄露记忆.例如,如果您将其绑定到数据集,但是在完成后您没有丢弃数据集,则可能会发生泄漏.您可能需要使用性能分析工具来识别未释放的可用资源.

顺便说一句,调用GC.Collect()是个坏主意.只是说.

  • +1`CG.Collect()`被调用经常是*编程错误的证据*(即使错误编程只是GC.Collect()调用本身) (4认同)
  • @Hogan - 准则非常明确:不要!当然有正当理由这样做,但这些原因存在极少数应用程序(这样的少数人,你可以放心地假设你不在其中).所以,再次*不要!*.GC是一个非常昂贵和缓慢的操作(特别是在.NET 4之前),它也是一个完全自动化的过程 - 所以手动调用它是浪费. (3认同)
  • @Hogan - 在另一个答案中指出,由于生成促销而调用GC.Collect()可能会使事情变得更糟. (2认同)

Jef*_*eff 11

Windows窗体应用程序中最常见的内存泄漏源是事件处理程序,在表单处理后仍保持连接...所以这是开始调查的好地方.像http://memprofiler.com/这样的工具可以帮助确定从未进行过GC的实例的根.

至于你对GC.Collect的调用

  1. 在实践中这样做绝对不好
  2. 为了确保您的GC收集确实尽可能地收集,您需要进行多次传递并等待挂起的终结器,因此该调用是真正同步的.

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    
    Run Code Online (Sandbox Code Playgroud)

此外,任何与您的表单实例相关的内容都会将表单保留在内存中,即使它已被关闭和处理后也是如此.

喜欢:

static void Main() {
    var form = new MyForm();
    form.Show();
    form.Close();

    // The GC calls below will do NOTHING, because you still have a reference to the form!
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    // Another thing to not: calling ShowDialog will NOT 
    // get Dispose called on your form when you close it.
    var form2 = new MyForm();
    DialogResult r = form2.ShowDialog();

    // You MUST manually call dispose after calling ShowDialog! Otherwise Dispose 
    // will never get called.
    form2.Dispose();

    // As for grids, this will ALSO result in never releasing the form in
    // memory, because the GridControl has a reference to the Form itself
    // (look at the auto-generated designer code).
    var form3 = new MyForm();
    form3.ShowDialog();
    var grid = form3.MyGrid;

    // Note that if you're planning on actually using your datagrid
    // after calling dispose on the form, you're going to have
    // problems, since calling Dipose() on the form will also call
    // dispose on all the child controls.
    form3.Dispose();
    form3 = null;
}
Run Code Online (Sandbox Code Playgroud)

  • 在第二次GC调用之前或等待挂起的终结器之前,它可能无法反映任务管理器中已释放的内存.您在调用ShowDialog后关于Disposing的声明不正确.请参阅Microsoft文章http://msdn.microsoft.com/en-us/library/system.windows.forms.form.showdialog%28VS.90%29下的"调用ShowDialog()"之后的"为什么必须Dispose()". ASPX (2认同)