mic*_*mus 18 .net c# memory-management idisposable
我正在开发一个项目,其中有大量的对象由一些类实例化,这些类在应用程序的生命周期内保留在内存中.每次不时抛出OutOfMemoryExceptions都会导致大量内存泄漏.似乎在实例化的对象之前超出范围之后,它们不会被垃圾收集.
我已经将问题分离为主要关于附加到永远不会分离的长生命对象的事件处理程序,从而导致长生命对象仍然具有对超出范围对象的引用,然后永远不会垃圾收集.
我的同事提出的解决方案如下:在所有类,全面和Dispose方法中实现IDisposable,使对象中的所有引用为null,并从您附加到的所有事件中分离.
我相信这是一个非常糟糕的主意.首先是因为它"过度杀戮",因为问题可以通过修复一些问题区域来解决,其次因为IDisposable的目的是释放对象控制的任何非托管资源,而不是因为你不信任垃圾收集器.到目前为止,我的论点都被置若罔闻.我怎么能说服他们这是徒劳的?
Dan*_*ker 16
巧合的是,我刚才在其他地方发表了这条评论
对错误保留的对象的引用仍然是资源泄漏.这就是为什么GC程序仍然可能有泄漏的原因,通常是由于Observer模式 - 观察者在列表上而不是observable并且永远不会被取消.最终,每个人
remove
都需要aadd
,就像每个人delete
都需要的一样new
.完全相同的编程错误,导致完全相同的问题."资源"实际上只是一对函数,必须使用相应的参数调用相同的次数,并且当您未能执行此操作时会发生"资源泄漏".
你说:
目的
IDisposable
是释放对象控制的任何非托管资源
现在,事件上的+=
和-=
运算符实际上是一对函数,您必须使用相应的参数调用相同的次数(事件/处理程序对是相应的参数).
因此它们构成了资源.由于GC没有为您处理(或"管理")它们,因此将它们视为另一种非托管资源会很有帮助.正如Jon Skeet在评论中指出的那样,非托管通常具有特定的含义,但在IDisposable
我认为有必要扩大它以包含任何资源之类的东西之后,必须在"建立起来"之后将其"拆除". .
因此,事件分离是处理的一个非常好的候选者IDisposable
.
当然,您需要在Dispose
某处调用,而不需要在每个对象上实现它(只需要那些需要管理的事件关系).
另外,请记住,如果一对对象通过一个事件连接,并且你"抛弃它们漂流",通过丢失所有其他对象中对它们的所有引用,它们就不会保持彼此活着.GC不使用引用计数.一旦物体(或物体岛)无法到达,它就会被收集起来.
您只需担心作为事件处理程序登记的对象,其中包含长时间存在的对象上的事件.例如AppDomain.UnhandledException
,应用程序主窗口上的静态事件或事件.
Jon*_*eet 11
将他们指向Joe Duffy关于IDisposable/finalizers的帖子 - 结合许多聪明人的智慧.
我现在发现很难看到那里的声明说"当你不需要它时不要实现它" - 但除了其他任何东西之外,向他们展示正确实施它的复杂性可能有助于阻止他们它...
不幸的是,如果人们不听,他们就不会听.试图让他们解释为什么他们认为他们需要IDisposable
.他们认为垃圾收集器不起作用吗?告诉他们它有效.如果你可以说服他们说它没有好处(对于大多数类型),那么他们肯定会停止为自己添加工作......
正如Brian所说,实现IDisposable
本身不会对事件问题有所帮助 - 它实际上需要通过某种东西来调用.在这种情况下,终结者也不会帮助你.他们确实需要明确地做一些事情来删除事件处理程序.
只是Dispose()
在所有类型中实施并不能解决您的问题.请记住,Dispose()
它不是自动调用的,它与回收托管内存无关.为了对您的Dispose()
方法产生任何影响,您需要在所有相关位置调用它 - 显式或通过using
.
换句话说,只是实现IDisposable
四处不会神奇地解决你的问题,因为Dispose()
除非你也改变代码中每种类型的用法,否则不会调用方法.
但是,我不建议IDisposable
在所有类型上实现,只是因为它没有意义.该接口用于指示所讨论的类型使用某些资源,该资源不由垃圾收集器处理.
事件引用由垃圾收集器处理.如果您的发布商的续航时间明显长于订阅者,则只需取消订阅即可.一旦发布者去世,订阅者也将死亡.
我曾经帮助同事解决了类似的问题,发生了OutOfMemoryException错误(由于事件导致对象引用挂起).我做的第一件事是通过FXCop运行代码,突出显示Dispose没有在IDisposable类上调用.
修改要处理的代码修复了问题.也许你应该推荐使用FXCop?
也许在源存储库中找到带有问题的代码,在其上运行FXCop-看它是否突出显示问题(如果它是由.NET Framework类引起的话可能会发生)并使用它来说服你的同事.