如果 MyCollection 无论如何都要收集垃圾,然后你不需要处理它。这样做只会使CPU超过必要的流失,甚至可能使垃圾收集器已经执行的一些预先计算的分析无效。
MyCollection
我用 IDisposable 做一些事情,比如确保正确处理线程,以及非托管资源。
IDisposable
的 编辑 强> 回应斯科特的评论:
GC性能指标受影响的唯一时间是调用[sic] GC.Collect()的时间。
从概念上讲,GC维护对象引用图的视图,以及从线程的堆栈帧对它的所有引用。这个堆可能非常大并且跨越许多页面的内存。作为优化,GC会缓存对不太可能经常更改的页面的分析,以避免不必要地重新扫描页面。当页面中的数据发生更改时,GC会从内核接收通知,因此它知道页面很脏并需要重新扫描。如果集合在Gen0中,那么页面中的其他内容可能也会发生变化,但这在Gen1和Gen2中的可能性较小。有趣的是,这些挂钩在Mac OS X中不适用于将GC移植到Mac以便在该平台上运行Silverlight插件的团队。
反对不必要的资源处置的另一点:想象一个过程正在卸载的情况。想象一下,这个过程已经运行了一段时间。有可能该进程的许多内存页面已被交换到磁盘。至少他们不再处于L1或L2缓存中。在这种情况下,卸载的应用程序没有必要将所有这些数据和代码页交换回内存,以“释放”当进程终止时将由操作系统释放的资源。这适用于托管甚至某些非托管资源。只有处理非后台线程活动的资源必须处理,否则进程将保持活动状态。
现在,在正常执行期间,必须正确清理短暂的资源(正如@fezmonkey指出的那样) 数据库连接,套接字,窗口句柄 )以避免非托管内存泄漏。这些是必须处理的事物。如果你创建一个拥有一个线程的类(并且拥有我的意思是它创建了它,因此负责确保它停止,至少通过我的编码风格),那么该类很可能必须实现 IDisposable 然后拆掉线程 Dispose 。
Dispose
.NET框架使用 IDisposable 接口作为信号,甚至警告,给开发人员这个类 必须 被处置。我想不出框架中实现的任何类型 IDisposable (不包括显式接口实现),其中dispos是可选的。
我不会重复关于使用或释放未受管理资源的常规内容,这些内容已全部涵盖。但我想指出似乎是一种常见的误解。 给出以下代码
Public Class LargeStuff Implements IDisposable Private _Large as string() 'Some strange code that means _Large now contains several million long strings. Public Sub Dispose() Implements IDisposable.Dispose _Large=Nothing End Sub
我意识到Disposable实现不符合当前的指导原则,但希望你们都能理解。 现在,当调用Dispose时,释放多少内存? 答:没有。 调用Dispose可以释放非托管资源,它不能回收托管内存,只有GC可以做到这一点。多数民众赞成不是说上述不是一个好主意,遵循上述模式实际上仍然是一个好主意。一旦运行了Dispose,就没有什么能阻止GC重新声明_Large正在使用的内存,即使LargeStuff的实例可能仍然在范围内。 _Large中的字符串也可能在gen 0中,但LargeStuff的实例可能是gen 2,因此,内存将更快地被重新声明。 添加finaliser来调用上面显示的Dispose方法没有意义。这将延迟重新声明内存以允许终结者运行。
如果你想 的 现在删除 强> , 使用 的 非托管内存 强> 。
看到:
处理管理资源的最合理用例是为GC准备回收原本无法收集的资源。
一个主要的例子是循环引用。
虽然最佳做法是使用避免循环引用的模式,但如果你最终得到(例如)一个“子”对象,该对象的引用又回到了它的“父”,那么如果你放弃它就可以停止父级的GC收集引用并依赖GC - 如果你已经实现了终结器,它将永远不会被调用。
绕过此方法的唯一方法是通过在子项上将Parent引用设置为null来手动中断循环引用。
在父级和子级上实现IDisposable是执行此操作的最佳方式。在Parent上调用Dispose时,在所有子项上调用Dispose,在子Dispose方法中,将Parent references设置为null。
您给出的代码示例不是一个很好的例子 IDisposable 用法。字典清除 一般 不应该去 Dispose 方法。当字典项超出范围时,它将被清除并处理。 IDisposable 实现需要释放一些即使在超出范围之后也不会释放/释放的内存/处理程序。
以下示例显示了IDisposable模式的一个很好的示例,其中包含一些代码和注释。
public class DisposeExample { // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class MyResource: IDisposable { // Pointer to an external unmanaged resource. private IntPtr handle; // Other managed resource this class uses. private Component component = new Component(); // Track whether Dispose has been called. private bool disposed = false; // The class constructor. public MyResource(IntPtr handle) { this.handle = handle; } // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. component.Dispose(); } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. CloseHandle(handle); handle = IntPtr.Zero; // Note disposing has been done. disposed = true; } } // Use interop to call the method necessary // to clean up the unmanaged resource. [System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle); // Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } } public static void Main() { // Insert code here to create // and use the MyResource object. } }
在您发布的示例中,它仍然不“立即释放内存”。所有内存都是垃圾回收,但它可能允许在早期收集内存 代 。你必须运行一些测试才能确定。
框架设计指南是指南,而不是规则。它们告诉您接口主要用于什么,何时使用它,如何使用它以及何时不使用它。
我曾经使用IDisposable读取了一个简单的RollBack()故障代码。下面的MiniTx类将检查Dispose()上的标志,如果是 Commit 电话永远不会发生,然后会打电话 Rollback 就自己而言。它添加了一层间接,使调用代码更容易理解和维护。结果看起来像:
Commit
Rollback
using( MiniTx tx = new MiniTx() ) { // code that might not work. tx.Commit(); }
我也看到计时/日志代码做同样的事情。在这种情况下,Dispose()方法停止计时器并记录该块已退出。
using( LogTimer log = new LogTimer("MyCategory", "Some message") ) { // code to time... }
所以这里有几个具体的例子,它们不做任何非托管资源清理,但是成功地使用了IDisposable来创建更干净的代码。
我看到很多答案已经转移到谈论对托管和非托管资源使用IDisposable。我建议将这篇文章作为我发现如何实际使用IDisposable的最佳解释之一。
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
对于实际问题;你应该使用IDisposable来清理占用大量内存的托管对象吗? 的 没有 强> 。原因是一旦你处理了IDisposable,你应该让它超出范围。此时,任何引用的子对象也超出范围并将被收集。
唯一真正的例外是,如果您在托管对象中占用了大量内存,并且您已阻止该线程等待某些操作完成。如果在完成调用之后不需要那些对象,那么将这些引用设置为null可能允许垃圾收集器更快地收集它们。但是这种情况会代表需要重构的坏代码 - 而不是IDisposable的用例。
如果有的话,我希望代码是 减 离开时有效率。
调用Clear()方法是不必要的,如果Dispose没有这样做,GC可能不会这样做......
大多数“非托管资源”讨论的一个问题是它们并没有真正定义术语,但似乎暗示它与非托管代码有关。虽然许多类型的非托管资源确实与非托管代码进行交互,但在这些术语中考虑非托管资源是没有用的。
相反,人们应该认识到所有管理资源的共同点:它们都需要一个对象,要求某些外部“事物”代表它做某事,损害其他一些“事物”,而另一个实体同意这样做直到另行通知。如果对象被抛弃并消失得无影无踪,那就没有什么可以告诉外面的“事物”它不再需要代表不再存在的对象来改变它的行为;因此,“事物的有用性将永久地减少。
因此,一个非托管资源代表一些外部“事物”的协议,以代表一个对象改变它的行为,如果该对象被抛弃并不再存在,那将无用地损害该外部“事物”的有用性。托管资源是这样一个协议的受益人,但是如果它被放弃则已经注册接收通知,并且在被销毁之前将使用这样的通知将其事务整理好。
除了它作为一种控制方式的主要用途 的 一生 强> 的 的 系统资源 强> (完全涵盖了令人敬畏的答案 伊恩 ,荣誉!), 的 IDisposable接口/使用 强> 组合也可以用来 的 范围(关键)全球资源的状态变化 强> : 安慰 , 线程 , 处理 , 任何 全球对象 像一个 应用实例 。
我写了一篇关于这种模式的文章: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
它说明了如何保护一些常用的全局状态 的 可重复使用 强> 和 的 可读 强> 方式: 控制台颜色 ,当前 线程文化 , Excel应用程序对象属性 ...
有些东西是 Dispose() 操作在示例代码中执行 威力 具有不会因正常的GC而发生的效果 MyCollection 宾语。
Dispose()
如果引用的对象 _theList 要么 _theDict 然后由其他对象引用 List<> 要么 Dictionary<> 对象不会被收集,但突然没有内容。如果示例中没有Dispose()操作,那些集合仍将包含其内容。
_theList
_theDict
List<>
Dictionary<>
当然,如果是这种情况,我会称之为破碎的设计 - 我只是指出(迂腐地,我想), Dispose() 操作可能不是完全冗余的,取决于是否有其他用途 List<> 要么 Dictionary<> 片段中未显示的内容。
方案我使用IDisposable:清理非托管资源,取消订阅事件,关闭连接
我用来实现IDisposable的成语( 不是线程安全的 ):
class MyClass : IDisposable { // ... #region IDisposable Members and Helpers private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { // cleanup code goes here } disposed = true; } } ~MyClass() { Dispose(false); } #endregion }
IDisposable 有利于取消订阅活动。
IDisposable 经常被用来利用 using 声明并利用一种简单的方法来对托管对象进行确定性清理。
using
public class LoggingContext : IDisposable { public Finicky(string name) { Log.Write("Entering Log Context {0}", name); Log.Indent(); } public void Dispose() { Log.Outdent(); } public static void Main() { Log.Write("Some initial stuff."); try { using(new LoggingContext()) { Log.Write("Some stuff inside the context."); throw new Exception(); } } catch { Log.Write("Man, that was a heavy exception caught from inside a child logging context!"); } finally { Log.Write("Some final stuff."); } } }
首先是定义。对我来说,非托管资源意味着一些类,它实现了IDisposable接口或使用dll调用创建的东西。 GC不知道如何处理这些对象。如果类仅具有值类型,那么我不认为此类是具有非托管资源的类。 对于我的代码,我遵循以下做法:
public class SomeClass : IDisposable { /// <summary> /// As usually I don't care was object disposed or not /// </summary> public void SomeMethod() { if (_disposed) throw new ObjectDisposedException("SomeClass instance been disposed"); } public void Dispose() { Dispose(true); } private bool _disposed; protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing)//we are in the first call { } _disposed = true; } }