IDisposable 实现 - “if(处置)”中应该包含什么内容?
我一直在修复 winforms 应用程序中的一些内存泄漏问题,并注意到一些未明确处理的一次性对象(开发人员尚未调用 Dispose 方法)。 Finalize 方法的实现也没有帮助,因为它不包含在 if (diswriting)
子句中。所有的静态事件注销和集合清除都放在if (dispose)
子句中。如果对象是一次性的,最佳实践是调用 Dispose,但不幸的是,
如果存在非托管对象、静态事件处理程序和一些在处置时需要清除的托管集合,有时会发生这种情况。如何决定 if (disusing)
子句中应包含哪些内容以及应包含哪些内容。
// 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)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
它说托管对象应该在 if (diswriting)
中,只有在显式时才正常执行调用 Dispose 方法开发商。如果 Finalize 方法已实现,并且开发人员忘记调用 Dispose 方法,则通过 Finalizer 到达此处的执行不会进入 if (diswriting)
部分。
以下是我的问题。
如果我有导致内存泄漏的静态事件处理程序,我应该在哪里取消注册它们?在
if (diswriting)
子句中还是之外?如果我有一些集合导致内存泄漏,我应该在哪里清除它们?在
if (diswriting)
子句中还是之外?如果我使用第三方一次性对象(例如:devExpress winform 控件),我不确定它们是托管对象还是非托管对象。假设我想在处理表单时处理它们。我如何知道什么是托管对象,什么是非托管对象?一次性不是这么说的吗?在这种情况下,如何决定
if (disducing)
子句中应包含哪些内容以及应删除哪些内容?如果我不确定某些托管或非托管的内容,在
if (diswriting)
子句中处置/清除/取消注册事件可能会产生什么不良后果?假设它在处置之前检查 null?
编辑
我的意思是事件取消注册如下所示。发布者是一个长期存在的实例,下面一行位于订阅者的构造函数中。在这种情况下,订阅者需要取消注册事件并在发布者之前进行处理。
publisher.DoSomeEvent += subscriber.DoSomething;
I have been fixing some memory leak issues in a winforms application and noticed some disposable objects that are not Disposed explicitly (developer hasn't called Dispose method). Implementation of Finalize method also doesn't help because it doesn't go in if (disposing)
clause. All the static event unregistering and collection clearing have been put in if (disposing)
clause. The best practice is calling the Dispose if the object is disposable, but unfortunately this happens sometimes
If there are unmanaged objects, static event handlers and some managed collections that needs to clear when disposing. What's the way to decide what should go in and what should go out of if (disposing)
clause.
// 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)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
It says managed objects should in if (disposing)
which executes normally only when explicitly call Dispose method by the developer. If the Finalize method has been implemented and developer forgets to call the Dispose method the execution that comes here through the Finalizer does not go in if (disposing)
section.
Below are my questions.
If I have static event handlers that causes memory leaks where should I un-register them? In or out of
if (disposing)
clause?If I have some collections that causes memory leaks where should I clear them? In or out of
if (disposing)
clause?If I am using third party disposable objects (eg: devExpress winform controls) that I am not sure whether they are managed or unmanaged objects. Let's say I want to dispose them when disposing a form. How can I know what are managed and what are non-managed objects? Being disposable doesn't say that? In such cases how to decide what should go in and what should go out of
if (disposing)
clause?If I am not sure something managed or unmanaged what can be the bad consequences of disposing/clearing/unregistering-events out of the
if (disposing)
clause? Let's say it checks for null before disposing?
Edit
What I mean as event un-registering is something like below. Publisher is a long lived instance and below line is in the subscriber's constructor. In this case subscriber need to unregister the event and dispose before the publisher.
publisher.DoSomeEvent += subscriber.DoSomething;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
概括地说,托管资源在 if (diswriting) 内部处置,非托管资源在其外部处置。处置模式的工作原理如下:
if (dispose) {
如果这个对象已经被释放,不要再次释放它。
如果(处置){
如果以编程方式请求处置(
true
),则处置该对象拥有的托管资源(IDisposable 对象)。如果处置是由垃圾收集器引起的(
false
),则不要处置托管资源,因为垃圾收集器可能已经处置了所拥有的托管资源,并且肯定会在垃圾收集器之前处置它们。应用程序终止。<代码>}
处置非托管资源并释放对它们的所有引用。步骤 1 确保这只发生一次。
处置 = true
将此对象标记为已处置,以防止重复处置。重复处置可能会在步骤 2 或 3 中导致 NullReferenceException。
问题 1
根本不要在
Dispose
方法中处置它们。如果您处置该类的多个实例会发生什么?您每次都会处理静态成员,尽管它们已经被处理过。我找到的解决方案是处理 AppDomain.DomainUnloaded事件并在那里执行静态处理。问题2
这完全取决于集合中的项目是托管的还是非托管的。可能值得创建托管包装器来为您正在使用的任何非托管类实现 IDisposable,以确保所有对象都受到管理。
问题3
IDisposable 是一个托管接口。如果一个类实现了 IDisposable,那么它就是一个托管类。在
if(处置)
中处置托管对象。如果它没有实现 IDisposable,则它要么是托管的,不需要处置,要么是非托管的,应该在if (diswriting)
之外处置。问题4
如果应用程序意外终止,或者不使用手动处置,垃圾收集器将以随机顺序处置所有对象。子对象可能会在其父对象被处置之前被处置,从而导致子对象被父对象第二次处置。大多数托管对象都可以安全地多次处置,但前提是它们已正确构建。如果多次处置某个对象,您可能会面临(尽管不太可能)导致 gargabe 收集失败的风险。
Broadly, managed resources are disposed inside
if (disposing)
and unmanaged resources outside of it. The dispose pattern works as such:if (disposed) {
If this object is already disposed, don't dispose of it a second time.
if (disposing) {
If disposal was requested programatically (
true
), dispose of managed resources (IDisposable objects) owned by this object.If disposal was caused by the garbage collector (
false
), do not dispose of managed resources because the garbage collector may have already disposed of the owned managed resources, and will definitelty dispose of them before the application terminates.}
Dispose of unmanaged resources and release all references to them. Step 1 ensures this only happens once.
disposed = true
Flag this object as disposed to prevent repeated disposal. Repeated disposal may cause a NullReferenceException at step 2 or 3.
Question 1
Don't dispose of them in the
Dispose
method at all. What would happen if you disposed of multiple instances of the class? You'd dispose the static members each time, despite them already being disposed. The solution I found was to handle the AppDomain.DomainUnloaded event and perform static disposal there.Question 2
It all depends if the items of the collection are managed or unmanaged. It's probably worth creating managed wrappers that implement IDisposable for any unmanaged classes you are using, ensuring all objects are managed.
Question 3
IDisposable is a managed interface. If a class implements IDisposable, it's a managed class. Dispose of managed objects inside
if (disposing)
. If it doesn't implement IDisposable, it is either managed and does not require disposing, or is unmanaged and should be disposed outside ofif (disposing)
.Question 4
If the application terminates unexpectedly, or doesn't use manual disposal, the garbage collector disposes of all objects in random order. The child object may be disposed before it's parent is disposed, causing the child to be disposed a second time by the parent. Most managed objects can safely be disposed multiple times, but only if they've been built correctly. You risk (though, unlikely) causing the gargabe collection to fail if an object is disposed multiple times.
这里要记住的关键是
IDisposable
的用途。它的工作是帮助您在对象被垃圾收集之前确定释放代码所持有的资源。这实际上就是 C# 语言团队选择关键字using
的原因,因为括号确定了应用程序所需的对象及其资源的范围。例如,如果您打开到数据库的连接,您希望释放该连接并在使用完毕后尽快关闭它,而不是等待下一次垃圾收集。这就是您实施 Disposer 的位置和原因。
第二种情况是协助处理非托管代码。实际上,这与对操作系统的 C++/C API 调用有关,在这种情况下,您有责任确保代码不泄露。由于 .Net 的大部分内容都是为了简单地 P/Invoke 到现有的 Win32 API 而编写的,因此这种情况很常见。任何封装操作系统资源的对象(例如互斥锁)都将实现一个 Disposer,以允许您安全且确定地释放其资源。
然而,这些API还会实现一个析构函数,以保证如果您没有正确使用资源,操作系统不会泄漏该资源。当你的终结器被调用时,你不知道你引用的其他对象是否已经被垃圾回收,这就是为什么对它们进行函数调用是不安全的(因为它们可能会抛出 NullReferenceException ) ),只有非托管引用(根据定义不能被垃圾收集)可供终结器使用。
希望能有所帮助。
The key to remember here is the purpose of
IDisposable
. It's job is to help you deterministically release resources that your code is holding, before the object is garbage collected. This is actually why the C# language team chose the keywordusing
, as the brackets determine the scope that the object and it's resources are required for by the application.For instance, if you open a connection to a database, you want to release that connection and close it ASAP after you have finished with it, rather than waiting for the next garbage collection. This is where and why you implement a Disposer.
The second scenario is to assist with unmanaged code. Effectively this is anything to do with C++/C API calls to the operating system, in which case you are responsible for ensuring that the code isn't leaked. As much of .Net is written to simply P/Invoke down to the existing Win32 API this scenario is quite common. Any object which encapsulates a resource from the operating system (e.g. a Mutex) will implement a Disposer to allow you to safely and deterministically release it's resources.
However these APIs will also implement a destructor, to guarantee that if you don't use the resource correctly that it will not be leaked by the operating system. When your finalizer is called, you do not know whether or not other objects you were referencing have already been garbage collected, which is why it is not safe to make function calls upon them (as they could throw
NullReferenceException
), only the unmanaged references (which by definition cannot be garbage collected) will be available to the finalizer.Hope that helps a bit.
如果我有导致内存泄漏的静态事件处理程序,我应该在哪里取消注册它们?在 if (disusing) 子句之内或之外?
在类级别使用静态事件处理程序的实例上调用 Dispose 方法。所以你不应该在处置中取消注册它们。通常,当类卸载时或在您派生不再需要该事件处理程序的应用程序执行期间的某个时刻,应该取消注册静态事件处理程序。
对于所有托管和非托管资源,更好地实现 IDisposable 模式。
看这里
http://msdn.microsoft.com/en-我们/库/fs2xkftw%28VS.80%29.aspx
和
C# 中的 Finalize/Dispose 模式
If I have static event handlers that causes memory leaks where should I un-register them? In or out of if (disposing) clause?
Dispose method is called on instances where as static event handler are used at class level. So you should not un-register them at all in dispose. Usually static event handler should be un-register when class is unloading or at some point during the execution of application you derive that this event handler is no more required.
For all manages and un-managed resources better implement IDisposable pattern.
See here
http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx
and
Finalize/Dispose pattern in C#
听起来您主要有托管对象,即实现 IDisposable 的对象。非托管代码类似于 IntPtr 或句柄,这通常意味着调用非托管代码或 P/Invoke 来访问它们。
正如 Maheep 指出的那样,Dispose 并不是为此而设计的。当一个对象完成接收事件时,它应该注销自己。如果这不可能,请考虑改用 WeakReferences。
除非这些集合包含需要处置的对象,否则这可能不应该进行处置。如果它们是一次性对象,那么它应该进入“如果处置”块,并且您应该对集合中的每个项目调用 dispose。
如果它实现了 IDisposable,它是托管的
当终结器调用时,您不应该访问其他托管代码对象,这就是“if(处置)”块之外的含义.
It sounds like you mainly have managed objects, i.e. objects that implement IDisposable. Unmanaged code would be things like IntPtr's or handles, which would normally mean calling unmanaged code or P/Invoke to get to them.
As Maheep pointed out, Dispose is not meant for this. When an object is done receiving events it should unregister itself. If that's not possible, consider using WeakReferences instead.
This probably shouldn't go in dispose unless these collections contain objects that need to be disposed. If they are disposable objects then it should go in the "if disposing" block and you should call dispose on each item in the collection.
If it implements IDisposable it's managed
You shouldn't access other managed code objects when called by the finalizer which is what being outside the "if (disposing)" block means.
除非类的唯一目的是封装一些废弃后需要清理的资源(*),否则它不应该有终结器,并且永远不应该使用 False 值调用 Dispose(bool),而是调用 Dispose (错误)应该没有效果。如果一个继承的类需要保存一个在废弃后需要清理的资源,那么它应该将该资源封装到一个专门用于该目的的对象中。这样,如果主对象被放弃,并且没有其他人持有对封装资源的对象的任何引用,则该对象可以执行其清理,而无需在额外的 GC 周期中保持主对象处于活动状态。
顺便说一句,我不喜欢微软对 Dispose 标志的处理。我建议非虚拟 Dispose 方法应使用 Interlocked.Exchange 的整数标志,以确保从多个线程调用 Dispose 只会导致处理逻辑执行一次。标志本身可以是私有的,但应该有一个受保护的和/或公共的 Dispose 属性,以避免要求每个派生类实现自己的处置标志。
(*) 资源不是某种特定类型的实体,而是一个松散的术语,它包含类可能要求某些外部实体代表其执行的任何操作,需要告诉该外部实体停止执行这些操作。最典型的是,外部实体会授予类对某些东西的独占使用权(可以是内存区域、锁、GDI 句柄、文件、套接字、USB 设备或其他任何东西),但在某些情况下,外部实体实体可能被要求肯定地做某事(例如,每次发生某事时运行事件处理程序)或持有某事(例如,线程静态对象引用)。 “非托管”资源是一种如果被放弃则不会被清理的资源。
顺便说一句,请注意,虽然 Microsoft 可能希望封装非托管资源的对象应该在废弃时清理它们,但对于某些类型的资源来说,这确实不切实际。例如,考虑一个在线程静态字段中存储对象引用的对象,并在释放该对象引用时清空该对象引用(该释放自然必须在创建该对象的线程上进行)。如果对象被放弃但线程仍然存在(例如在线程池中),则线程静态引用的目标可以轻松地无限期地保持活动状态。即使没有任何对废弃对象的引用,因此其 Finalize() 方法运行,废弃对象也很难找到并销毁位于某个线程中的线程静态引用。
Unless the sole purpose of a class is to encapsulate some resource(*) which needs to be cleaned up if abandoned, it shouldn't have a finalizer, and Dispose(bool) should never be called with a value of False, but calling Dispose(False) should have no effect. If an inherited class would need to hold a resource requiring cleanup if abandoned, it should encapsulate that resource into an object devoted solely to that purpose. That way, if the main object gets abandoned and nobody else holds any reference to the object encapsulating the resource, that object can perform its cleanup without having to keep the main object alive for an extra GC cycle.
Incidentally, I dislike Microsoft's handling of the Disposed flag. I would suggest that the non-virtual Dispose method should use an integer flag with Interlocked.Exchange, to ensure that calling Dispose from multiple threads will only result in the dispose logic being performed once. The flag itself may be private, but there should be a protected and/or public Disposed property to avoid requiring every derived class to implement its own disposal flag.
(*) A resource isn't some particular type of entity, but rather a loose term that encompasses anything a class may have asked some outside entity to do on its behalf, which that outside entity needs to be told to stop doing. Most typically, the outside entity will have granted the class exclusive use of something (be it an area of memory, a lock, a GDI handle, a file, a socket, a USB device, or whatever), but in some cases the outside entity may have been asked to affirmatively do something (e.g. run an event handler every time something happens) or hold something (e.g. a thread-static object reference). An "unmanaged" resource is one which will not be cleaned up if abandoned.
BTW, note that while Microsoft may have intended that objects which encapsulate unmanaged resources should clean them up if abandoned, there are some types of resources for which that really isn't practical. Consider an object that stores an object reference in a thread-static field, for example, and blanks out that object reference when it is Dispose'd (the disposal would, naturally, have to occur on the thread where the object was created). If the object gets abandoned but the thread still exists (e.g. in the threadpool), the target of the thread-static reference could easily be kept alive indefinitely. Even if there aren't any references to the abandoned object so its Finalize() method runs, it would be difficult for the abandoned object to locate and destroy the thread-static reference sitting in some thread.
所有托管对象 均应包含在 if(dispose) 子句中。托管对象不应该超出它的范围(这将通过终结执行)。
原因是,如果该类具有析构函数,则垃圾收集器终结过程可以执行 Dispose(false)。通常只有存在非托管资源时才会有析构函数。垃圾收集器的终结没有特定的顺序来执行 Finalize 方法。因此,当终结发生时,其他托管对象可能不在内存中。
All Managed objects should go inside if(disposing) clause. Managed objects should not go out side of it (which will be executed through the finalization).
The reason is that Garbage collectors finalization process can execute the Dispose(false) if that class has a Destructor. Normally there is a Destructor only if there are unmanaged resources.Garbage collector's finalization doesn't have a particular order to execute the Finalize method. So, other managed objects may not be in memory by the time finalization occurs.