【问题标题】:c# memory allocation and deallocation patternsc#内存分配和释放模式
【发布时间】:2011-02-12 15:45:45
【问题描述】:

由于 C# 使用垃圾收集。什么时候需要使用 .Dispose 来释放内存?

我知道有几种情况,所以我会尝试列出我能想到的。

  1. 如果我关闭一个包含 GUI 类型对象的表单,这些对象是否会被取消引用并因此被收集?
  2. 如果我使用 new 创建一个本地对象,我应该在方法退出之前处理它还是让 GC 处理它?在这种情况下有什么好的做法?
  3. 在哪些情况下强制 GC 是可以理解的?
  4. GC 是否在收集对象时收集事件?

【问题讨论】:

标签: c# .net memory-management memory-leaks


【解决方案1】:

看看这个问题:

Is there a common practice how to make freeing memory for Garbage Collector easier in .NET?

如果您的类实例化 IDisposable 接口,那(可能)意味着它具有必须直接释放的系统资源。一种简单的方法是使用 using 关键字,如下所示:

using(var g = Graphics.FromBitmap(bmp))
{
    //Do some stuff with the graphics object
}

根据@Matt S 在我引用的那个问题中的回答。

对于您的问题:

  1. 如果实例化具有 IDisposable 的对象,则需要在关闭窗体时释放它。这在 WPF 中很棘手,在 Winforms 中很简单,因为 winforms 对话框具有 Dispose 方法。对于 WPF,我通过保留 WPF 类但隐藏的方式解决了这个问题,称为处置所有对象(如串行端口)的 dispose 方法,然后将 WPF 类设置为 null。
  2. 没有。让 GC 处理它。
  3. 我是这么认为的,但我被反对票否决了 :) 当我完成了非常大的分配时,强制 GC 删除它们是,imo,一个好主意。
  4. 我不确定。我认为事件本身就是对象,因此在不再使用时会被收集。

【讨论】:

  • 您的#2 回复似乎与其他所有人的回答相冲突。你为什么说让GC来处理它?它似乎也与您的原始陈述有些冲突?
  • 如果你只是使用new创建一个对象,但它没有实现IDisposable接口,让GC来处理它。但是,如果它实现了 IDisposable,那么您必须在释放它之前将其处理掉,如 #1 所述。如果你声明一个新的int数组,int不实现IDisposable,所以你可以让gc来处理它。
【解决方案2】:

理论上,如果您已正确定义组件,则永远需要对您的对象调用 Dispose(),因为终结器应该最终处理它.

话虽如此,只要您使用实现 IDisposable 的对象,最好在处理完对象后立即调用该对象的 Dispose()。

对于您的一些具体观点:

1) 如果你知道你已经“完成”了表单,你可以调用 Dispose() 。这将在那个时间点强制清理与表单关联的非托管资源。

2) 在这种情况下:如果您的对象只是在该方法中使用,请改用“使用”:

using (MyObject myObject = new MyObject())
{
   // use your object
} // It'll be disposed of here for you

3) 这样做的原因很少,但总的来说,没有。

4) 事件是一个委托 - 与委托关联的内存将在委托本身变为无根后被收集,这通常发生在相关对象无根时。

【讨论】:

  • 我确实对所有 IO 类型资源使用 using() {} 模式。我将开始将它用于所有一次性对象以遵循一致的模式。谢谢你的回答。
【解决方案3】:

您应该在每个实现 IDisposable 的类上调用Dispose。如果它不需要是 Dispose ed,那么它就不会实现 IDisposable

至于你的其他问题:

  1. 当您将控件添加到 Form 的 Controls 集合时,该控件将在表单关闭时自动释放,因此您无需在此处执行任何操作。
  2. 如果对象实现了IDisposable,那么你需要调用Dispose。例如,如果您转到new FileStream(...),则需要释放 FileStream,因为它实现了 IDisposable。我建议您阅读 C# 中的 using 构造,它可以更轻松地处理 IDisposable 对象。
  3. 并非如此,99.99% 的时间,垃圾收集器会知道最佳运行时间是什么时候。这是一种“你会知道什么时候需要它”的情况。
  4. 当不再引用包含该事件的对象时,从逻辑上讲,事件中包含的任何对象引用也不再被引用,并且可以被收集。

【讨论】:

  • #1:是的,但是如果在设计时添加了对象但没有添加到控件的集合中怎么办?表单关闭时它是否仍会被释放? #2:事实上,在处理 IO 资源时,我几乎总是实现 using(){} 模式。 #4:我读过很多没有删除属性的事件会“泄漏”内存的实例。或者更确切地说不释放内存。我意识到这可能只是一个可以忽略不计的参考。
  • 在#4上,当包含事件的对象不再被引用时,引用事件本身持有的对象也将不再被引用。当您向事件添加处理程序时,问题就出现了,该事件包含对该对象的引用:因此在回收 event 或您手动取消订阅之前,不会回收该对象。对于大多数 WinForms 的东西,事件处理程序通常在包含表单中,所以通常不是问题。
  • 啊,我明白了。感谢您的澄清。
【解决方案4】:

如果您使用IDisposable 对象,请考虑使用using 语句为您自动处理处置。

【讨论】:

    【解决方案5】:

    就像 Reed Copsey 所说,通常不需要调用 Dispose。

    可能导致您的问题的一个可能情况是静态对象持有其他不再使用的其他对象的引用。以下代码显示了一个示例:

    Form_Load(...)
        MyState.Instance.AddressChanged += this.User_AddressChanged;
    End
    

    如果由于某种原因,当表单被卸载时,代码没有取消注册事件处理程序,表单实例仍然会被状态对象引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-28
      • 2020-08-25
      • 1970-01-01
      • 2023-03-03
      • 2016-02-19
      相关资源
      最近更新 更多