我在一个项目中,有通过保留在内存中的应用程序的生命周期几班正在实例化一个巨大的物体的数量工作。 有很多内存泄漏的是与被抛出每OutOfMemoryExceptions现在又引起。 这似乎是实例化对象前超出范围后,他们没有被垃圾收集。
我已隔离问题是大部分是关于附加到被从不脱离的长活对象的事件处理程序,从而使所述长寿命的目的是仍然具有到范围外的对象,然后将永远不会成为一个参考垃圾收集。
已经提出由我的同事的解决方案如下:对所有类实现IDisposable,全线和Dispose方法,空所有在你的对象的引用,并从您连接到所有的事件分开。
我相信这是一个非常非常糟糕的主意。 首先是因为它的“矫枉过正”,因为该问题可以通过固定的几个问题领域进行大多解决,其次是因为IDisposable接口的目的是为了释放任何非托管资源的对象的控制,不会因为你不信任的垃圾收集器。 到目前为止,我的论点置若罔闻。 我怎样才能说服他们,这是徒劳的?
Answer 1:
巧合的是我刚刚发布此评论其它地方:
到对象的参考被不正确地保留仍然是一个资源泄漏。 这就是为什么GC程序仍然可以有泄漏,通常是由于观察者模式 - 观察者是一个列表,而不是观察到的和永远不会被取出它。 最终, remove
,需要为每个add
,就像delete
,需要为每一个new
。 一模一样的编程错误,恰恰导致了同样的问题。 “资源”是一个非常公正的一对必须调用次数相等数与相应参数的函数,以及“资源泄漏”是当你不这样做会发生什么。
你说:
目的IDisposable
是释放任何非托管资源的对象控件
现在, +=
和-=
上一个事件运算符是有效的一对你必须调用的具有相应参数次相等数目的(事件/处理程序对是对应的参数)的功能。
因此,他们构成了资源。 而且,因为它们不与处理(或“管理”),由GC对你来说,它可以把它们作为只是另一种非托管资源的帮助。 作为乔恩斯基特在评论中指出,非托管通常有特定的含义,但在的情况下IDisposable
,我认为它有助于扩大到包括任何资源一样,没有被“推倒”后,它已经“建成”。
因此,事件剥离是一个很好的候选人与处理IDisposable
。
当然,你需要调用Dispose
的地方,而你并不需要实现它的每一个对象(只是那些需要管理事件的关系)上。
此外,记住,如果一对对象由事件连接,并且你“扮演他们漂泊”,由失去了其他东西,他们的所有引用,他们没有留住对方活着。 GC不使用引用计数。 一旦对象(或对象的岛)不可达时,它是由用于被收集。
你只需要担心入伍与对生活很长一段时间中对象的事件的事件处理程序的对象。 例如静态事件,如AppDomain.UnhandledException
,或应用程序的主窗口上的事件。
Answer 2:
在点他们乔·达菲的一篇关于IDisposable接口/终结许多聪明人的集体智慧- 。
我目前发现很难看到一份声明中说,有“不执行它时,你不需要它” - 从任何东西,但不谈,显示他们的参与执行它的复杂性,可能会很好帮助从劝阻它...
不幸的是,如果人们不听,他们不听。 试图让他们解释为什么他们认为他们需要IDisposable
。 难道他们认为垃圾回收器不能正常工作? 让他们知道它的工作原理。 如果你能说服他们,它做的不好(大多数类型),那么他们肯定会停止添加为自己工作...
布赖恩说,实施IDisposable
是不会帮助它自己的事件问题-它需要实际的东西来调用 。 终结是不会帮助你在这种情况下,无论是。 他们真的需要明确地做一些事来移除事件处理程序。
Answer 3:
仅仅实现Dispose()
在所有类型是不会解决你的问题。 请记住, Dispose()
不会自动调用,它没有任何关系与回收管理内存。 为了让你的任何影响Dispose()
方法,你需要把它调用的所有相关地方-任何明示或通过using
。
在刚刚实施换句话说IDisposable
各地不会神奇地解决你的问题引起Dispose()
方法将不会被调用,除非你还在你的代码改变每类型的使用。
但是,我不建议实施IDisposable
上所有类型的,只是因为它是没有意义的。 所述接口被用来指示所讨论的类型使用一些资源,这是不被垃圾收集处理。
事件引用由垃圾收集处理。 你只需要退订,如果你的发行住显著长于你的用户。 一旦出版商去世的用户会死为好。
Answer 4:
我曾经帮一个同事出去与OutOfMemoryException异常错误存在的类似的问题(由于离开的物体引用游逛事件)。 我做的第一件事是贯穿的FxCop代码,强调处置不叫上了IDisposable类。
修改代码以设置固定的问题。 也许你应该推荐的FxCop使用?
也许找到与源库问题上的代码,运行的FxCop上它 - 看看它突出的问题(它可能会,如果它是由.NET Framework类引起的),并用它来说服你的同事。
Answer 5:
这是一个很难拉过,但我发现让人们做你想要的东西,就是让他们认为这是他们的想法的最好办法。 你知道他们比我好,但像“如果只是有办法找出原因的对象是挂在这么长”,“我希望我能知道更多有关事件的持有对象”可能是一个起点。
Answer 6:
问他们是否愿意被强制使用自行车后关闭马达,即使没有一个电动机。
或者,如果他们想迫使他们离开之前按自己的椅子,桌子,一杯咖啡和其他的东西了“关闭”按钮,他们的工作场所,即使没有什么要被关闭。
实现了IDisposable迫使用户明确地告诉对象时不再使用它。 如果这个对象不需要清理东西,它只是一个不必要的复杂性。
顺便说,通过实施了IDisposable恕我直言注销事件是清理活动以适当的方式。 也有一些是为“关闭”。
Answer 7:
IDisposable的仅意在用于释放资源(SafeHandles等),和Dispose方法传播通过类层次结构。 这并不意味着,试图避开不好的非托管编程实践。
Answer 8:
在解决一个更OutOfMemoryException异常创建任务。 当有人解析错误属于别人,它提出了对自己的代码的责任。 在我们项目的前期我们一直推荐的“傻瓜的标志” - 负责对致命错误得到这个标志了一天。
Answer 9:
提出了一种解决方案,比他们的解决方案大大优于;-)
例如,一个简单的替代可能是使用WeakReference
S IN长期生活的对象来保存对事件处理程序的引用。 这就要求在事件处理程序,只要他们需要参考其他地方,但只要他们会走出去的范围,他们是垃圾回收和弱引用可以从长期生活的对象被删除。
Answer 10:
一般来说,如果你有一个事件注册一个处理程序,然后选择“注销”只是那种基本清理每个对象应在被摧毁做。 如果你没有在析构函数语言,那么你必须定义一个方法调用来告诉它要离开的那个目标。 这种方法应该清理的事件处理程序。
这不是什么IDisposable的是? 为什么你需要另一种解决办法,如果它已经存在? 为什么没有那些knuckleheads正确执行摆在首位的对象? ;)
Answer 11:
一个对象需要实现IDisposable,如果它需要确保的东西可能得到之前被抛弃清理。 一些对象的属性被‘连接’到其他的目的,因此在改变这些属性会改变其他对象; 有时需要给这些属性设置为null。 在vb.net中,“WITHEVENTS”字段实际上是一种连接和断开事件处理程序,因此WITHEVENTS在vb.net字段应该设置为Nothing的属性。 请注意,它通常是没有用的对象纯粹实现IDisposable的归零出了自己领域的目的。 有一些情况下,如果它已经存在了很长一段时间的对象都保存最近创建的对象的引用,可能是有帮助的(例如,清除参考可以允许更快地收集到的最近创建的对象会比否则),但它肯定不是必要的。
什么是必要的,以确保其需要清理实现IDisposable,并保证其他对象的对象得到清理。 我在微软气恼鼓励人们写代码,放弃事件处理程序。 虽然这是事实,一个可以逃脱放弃事件处理程序在情况下,事件发布者生存期不会超过用户,这种情况下,与相互关联的GUI元素通常发生,我实在看不出有任何理由情况下都不得应该总是清理是理所当然的事。
文章来源: How do I convince my colleagues not to implement IDisposable on everything? [closed]