当您确保所有句柄,实现的东西IDispose
都被丢弃时,在托管系统中是否有可能泄漏内存?
是否会出现遗漏某些变量的情况?
事件处理程序是非显而易见的内存泄漏的常见来源.如果你从object2订阅了object1上的一个事件,那么就执行object2.Dispose()并假装它不存在(并从你的代码中删除所有引用),在object1的事件中有一个隐式引用会阻止object2被垃圾收集.
MyType object2 = new MyType(); // ... object1.SomeEvent += object2.myEventHandler; // ... // Should call this // object1.SomeEvent -= object2.myEventHandler; object2.Dispose();
这是泄漏的常见情况 - 忘记轻松取消订阅事件.当然,如果收集了object1,也会收集object2,但直到那时才收集.
我不认为C++风格的内存泄漏是可能的.垃圾收集器应该考虑到这些.即使永远不再使用对象,也可以创建聚合对象引用的静态对象.像这样的东西:
public static class SomethingFactory { private static ListlistOfSomethings = new List (); public static Something CreateSomething() { var something = new Something(); listOfSomethings.Add(something); return something; } }
这是一个明显愚蠢的例子,但它相当于托管运行时内存泄漏.
正如其他人所指出的那样,只要内存管理器中没有实际的错误,不使用非托管资源的类就不会泄漏内存.
你在.NET中看到的不是内存泄漏,而是永远不会被丢弃的对象.只要垃圾收集器可以在对象图上找到它,就不会丢弃对象.因此,如果任何生物对象都有对象的引用,它就不会被处理掉.
事件注册是实现这一目标的好方法.如果一个对象注册了一个事件,那么它注册的任何事件都会引用它,即使你删除了对该对象的所有其他引用,直到它取消注册(或者它注册的对象变得无法访问)它将保持活跃状态.
因此,您必须在不知情的情况下注意注册静态事件的对象.ToolStrip
例如,一个漂亮的功能是,如果您更改显示主题,它将自动在新主题中重新显示.它通过注册静态SystemEvents.UserPreferenceChanged
事件来完成这个漂亮的功能.当您更改Windows主题时,会引发该事件,并且所有ToolStrip
正在侦听该事件的对象都会收到有关新主题的通知.
好吧,假设你决定扔掉ToolStrip
你的表格:
private void DiscardMyToolstrip() { Controls.Remove("MyToolStrip"); }
你现在有一个ToolStrip
永远不会死的.即使它不再出现在你的表单上,每次用户更改主题时,Windows都会尽职尽责地告诉ToolStrip
它不存在.每次垃圾收集器运行时,它都会认为"我不能扔掉那个物体,UserPreferenceChanged
事件正在使用它".
那不是内存泄漏.但它可能也是.
这样的事情使记忆探测器变得无价.运行一个内存分析器,你会说"这很奇怪,ToolStrip
堆上似乎有一万个对象,即使我的表单上只有一个.这是怎么发生的?"
哦,如果你想知道为什么有些人认为财产制定者是邪恶的:ToolStrip
要从UserPreferenceChanged
事件中取消注册,请将其Visible
属性设置为false
.
代表可能导致不直观的内存泄漏.
每当您从实例方法创建委托时,对该实例的引用都存储在该委托中.
此外,如果将多个委托组合成一个多播委托,那么只要在某处使用多播委托,就会有大量对大量对象的引用,这些对象不会被垃圾回收.
如果您正在开发一个WinForms应用程序,那么一个微妙的"泄漏" Control.AllowDrop
属性(用于启用拖放).如果AllowDrop
设置为"true",CLR仍将保留在您的控件上System.Windows.Forms.DropTarget
.要解决此问题,请确保您Control
的AllowDrop
属性设置为false
不再需要时,CLR将负责其余部分.
如前所述,保持参考将导致随着时间的推移增加内存使用.进入这种情况的一个简单方法是使用事件.如果你有一个长生命对象,其他对象听到的事件,如果从不删除监听器,那么长寿命对象上的事件将使这些其他实例在不再需要它们之后保持很长时间.
.NET应用程序中内存泄漏的唯一原因是,尽管它们的生命周期已经结束,但仍然会引用对象.因此,垃圾收集器无法收集它们.它们成为长寿的物体.
我发现,当对象的生命结束时,通过订阅事件而不取消订阅它很容易导致泄漏.
您可能会发现我的新文章很有用:如何检测并避免.NET应用程序中的内存和资源泄漏
反射发射是另一个潜在的泄漏源,例如内置的对象反序列化器和花哨的SOAP/XML客户端.至少在早期版本的框架中,从属AppDomains中生成的代码从未被卸载过......