防止具有添加行为的内存泄漏
我在WPF应用程序中创建了一个"添加行为",使我可以处理Enter键并移至下一个控件。我将其称为EnterKeyTraversal.IsEnabled,我们可以在我的博客上查看代码。
我现在主要担心的是,由于我正在处理UIElements上的PreviewKeyDown事件,并且从不显式"摘机"该事件,因此可能会发生内存泄漏。
防止这种泄漏的最佳方法是什么(如果确实存在)?我是否应该保留要管理的元素的列表,并在Application.Exit事件中取消预告事件PreviewKeyDown?是否有人在自己的WPF应用程序中成功完成了添加行为,并提出了一种优雅的内存管理解决方案?
解决方案
回答
确保事件引用元素与它们所引用的对象一起使用,例如表单控件中的文本框。或者,如果无法避免。在全局帮助程序类上创建一个静态事件,然后监视全局帮助程序类中的事件。如果无法完成这两个步骤,请尝试使用WeakReference,它们通常很适合这些情况,但它们会带来开销。
回答
@Nick是的,附带行为的是,根据定义,它们与我们要处理其事件的元素不在同一个对象中。
我认为答案就在于以某种方式使用WeakReference,但是我还没有看到任何简单的代码示例可以向我解释。 :)
回答
我刚刚读了博客文章,我认为我们有一些误导性建议,Matt。如果此处存在实际的内存泄漏,则说明这是.NET Framework中的错误,并且不一定是我们可以在代码中修复的错误。
我认为我们(以及博客的发布者)实际上在这里谈论的并不是泄漏,而是内存的持续消耗。那不是同一回事。为了清楚起见,泄漏的内存是由程序保留,然后被放弃(即,指针悬空)并随后无法释放的内存。由于内存是在.NET中管理的,因此从理论上讲这是不可能的。但是,程序可以保留不断增加的内存,而又不允许对它的引用超出范围(并有资格进行垃圾回收);但是该内存没有泄漏。程序退出后,GC会将其返回给系统。
所以。要回答问题,我认为我们实际上在这里没有问题。我们当然没有内存泄漏,并且从代码来看,就内存消耗而言,我也不用担心。只要确保不取消分配事件处理程序就不会重复分配该事件处理程序(即,我们只能设置一次,或者每次分配时都将其删除一次),我们似乎在做,代码应该没问题。
这似乎是我们博客上的发布者试图给建议,但是他使用了令人震惊的工作"泄漏",这是一个令人恐惧的词,但许多程序员却忘记了托管世界的真正含义。它不适用于这里。
回答
我不同意DannySmurf
一些WPF布局对象可能会阻塞内存,并在不进行垃圾回收时使应用程序真正变慢。因此,我发现选择的单词是正确的,我们正在将内存泄漏给不再使用的对象。我们希望这些项目被垃圾回收,但不是,因为在某处有一个引用(在这种情况下,来自事件处理程序)。
现在是一个真正的答案:)
我建议我们阅读MSDN上的WPF性能文章
Not Removing Event Handlers on Objects may Keep Objects Alive The delegate that an object passes to its event is effectively a reference to that object. Therefore, event handlers can keep objects alive longer than expected. When performing clean up of an object that has registered to listen to an object's event, it is essential to remove that delegate before releasing the object. Keeping unneeded objects alive increases the application's memory usage. This is especially true when the object is the root of a logical tree or a visual tree.
他们建议我们研究弱事件模式
另一种解决方案是在完成对象处理后删除事件处理程序。但是我知道,对于添加属性,这一点可能并不总是很清楚。
希望这可以帮助!
回答
我们是否实施了"弱事件模式"而不是常规事件?
- WPF中的弱事件模式
- 弱事件模式(MSDN)
回答
@Arcturus:
... clog up your memory and make your application really slow when they are not garbage collected.
这真是令人眼花I乱,我也不同意。然而:
...you are leaking memory to object that you no longer use... because there is a reference to them.
"将内存分配给程序,并且由于程序逻辑缺陷,该程序随后失去了访问它的能力"(维基百科,"内存泄漏")
如果有一个对象的活动引用,程序可以访问该对象,则根据定义,它不会泄漏内存。泄漏意味着对象(我们或者OS /框架)将不再可访问,并且在操作系统当前会话的整个生命周期内都不会释放该对象。这里不是这种情况。
(对不起,这是一个语义纳粹……也许我有点老派了,但是泄漏具有非常特殊的含义。这些天,人们倾向于使用"内存泄漏"来表示任何东西,它们消耗比他们想要的更多2KB的内存。 ..)
但是,当然,如果我们不释放事件处理程序,则在关闭进程之前,垃圾收集器将进程的内存回收之前,不会释放添加到该事件处理程序的对象。但是,这种行为完全是预期的,与我们似乎暗示的相反。如果我们希望回收一个对象,则需要删除所有可能使引用保持活动状态的内容,包括事件处理程序。
回答
是的,我知道在过去,内存泄漏是一个完全不同的主题。但是对于托管代码,"内存泄漏"一词的新含义可能更合适...
微软甚至承认这是内存泄漏:
Why Implement the WeakEvent Pattern? Listening for events can lead to memory leaks. The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. For instance, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler). This technique creates a strong reference from the event source to the event listener. Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that influenced by the object lifetime for the source (unless the event handler is explicitly removed). But in certain circumstances you might want the object lifetime of the listener to be controlled only by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.
我们将WPF用于具有大型ToolWindows的客户端应用程序,可以将其拖放,所有漂亮的东西,并且都与XBAP兼容。因为它仍然依赖于事件侦听器。.现在,当我们关闭窗口并关闭应用程序时,这可能不是问题。但是,如果我们要使用许多命令创建非常大的ToolWindows,并且一遍又一遍地重新评估所有这些命令,那么人们必须整天使用应用程序。和我们应用的响应时间。
另外,我发现向我的经理解释我们有内存泄漏要比向他解释一些对象由于某些需要清理的事件而没有被垃圾回收要容易得多;)
回答
我当然可以理解并表示同情(经理位)。
但是无论微软怎么称呼,我都不认为"新"定义是合适的。这很复杂,因为我们并不生活在一个100%受管的世界中(即使Microsoft喜欢假装我们这样做,但Microsoft本身并不生活在这样一个世界中)。当我们说内存泄漏时,我们可能意味着程序正在消耗过多的内存(这是用户的定义),或者直到退出后才会释放托管引用(如此处所示),或者未正确清理非托管引用上升(这可能是真正的内存泄漏),或者从托管代码调用的非托管代码正在泄漏内存(另一个实际泄漏)。
在这种情况下,即使我们不精确,"内存泄漏"的含义也很明显。但是,与某些人的交谈变得非常乏味,他们称每一次过度消耗或者未能收集到内存泄漏为例。当这些人是程序员,他们本应了解得更多的时候,这真令人沮丧。我认为技术术语具有明确的含义是很重要的。当他们这样做时,调试非常容易。
反正。并不是要把它变成关于语言的童话般的讨论。只是说...
回答
对,对,
我们当然是对的。但是,这个世界诞生了新一代的程序员,他们永远不会接触非托管代码,而且我确实相信语言定义会一遍又一遍地重新发明自己。 WPF中的内存泄漏以这种方式不同于C / Cpp。
当然,对我的经理们来说,我将其称为内存泄漏。.对我的同事们,我将其称为性能问题!
关于Matt的问题,可能是我们需要解决的性能问题。如果仅使用几个屏幕并使这些屏幕控件成为单例,则可能根本看不到此问题;)。