我在我的WPF应用程序中创建了一个“附加行为”,它允许我处理Enter按键并移动到下一个控件。我将其称为EnterKeyTraversal.IsEnabled,您可以在我的博客上看到代码 这里。
我现在主要担心的是我可能有内存泄漏,因为我在UIElements上处理PreviewKeyDown事件并且从未明确地“解开”事件。
什么是防止这种泄漏的最佳方法(如果有的话)?我应该保留我正在管理的元素列表,并在Application.Exit事件中取消挂起PreviewKeyDown事件吗?有没有人在他们自己的WPF应用程序中成功附加行为,并提出了一个优雅的内存管理解决方案?
我不同意DannySmurf
一些WPF布局对象可能会阻塞您的内存,并且当您的应用程序没有被垃圾回收时,它们会非常慢。因此,我发现选择的单词是正确的,您正在将内存泄露给不再使用的对象。您希望这些项目是垃圾收集的,但它们不是,因为某处有一个引用(在本例中是来自事件处理程序)。
现在回答真实的答案:)
我建议你读这个 MSDN上的WPF性能文章
不删除对象上的事件处理程序
可以保持对象活着
对象传递给的委托
它的事件实际上是一个参考
到那个对象。因此,事件
处理程序可以使对象保持更长时间
比预期。进行清洁时
注册的对象的一部分
听一个对象的事件,它是
删除该委托至关重要
在释放对象之前。保持
不需要的物体活着增加了
应用程序的内存使用情况。这是
当对象是。时尤其如此
逻辑树或视觉的根
树。
他们建议你调查一下 弱事件模式
另一种解决方案是在完成对象后删除事件处理程序。但我知道,对于附加属性,这一点可能并不总是很清楚。
希望这可以帮助!
我不同意DannySmurf
一些WPF布局对象可能会阻塞您的内存,并且当您的应用程序没有被垃圾回收时,它们会非常慢。因此,我发现选择的单词是正确的,您正在将内存泄露给不再使用的对象。您希望这些项目是垃圾收集的,但它们不是,因为某处有一个引用(在本例中是来自事件处理程序)。
现在回答真实的答案:)
我建议你读这个 MSDN上的WPF性能文章
不删除对象上的事件处理程序
可以保持对象活着
对象传递给的委托
它的事件实际上是一个参考
到那个对象。因此,事件
处理程序可以使对象保持更长时间
比预期。进行清洁时
注册的对象的一部分
听一个对象的事件,它是
删除该委托至关重要
在释放对象之前。保持
不需要的物体活着增加了
应用程序的内存使用情况。这是
当对象是。时尤其如此
逻辑树或视觉的根
树。
他们建议你调查一下 弱事件模式
另一种解决方案是在完成对象后删除事件处理程序。但我知道,对于附加属性,这一点可能并不总是很清楚。
希望这可以帮助!
除了哲学辩论之外,在查看OP的博客文章时,我没有看到任何泄漏:
ue.PreviewKeyDown += ue_PreviewKeyDown;
很难参考 ue_PreviewKeyDown
存储在 ue.PreviewKeyDown
。
ue_PreviewKeyDown
是一个 STATIC
方法并不能 GCed
。
没有硬性参考 ue
正在存储,所以没有任何东西阻止它存在 GCed
。
那么......泄漏在哪里?
是的,我知道在过去,Memory Leaks是一个完全不同的主题。但是对于托管代码,内存泄漏一词的新含义可能更合适......
微软甚至承认它是一个内存泄漏:
为什么要实现WeakEvent模式?
倾听事件可能导致
内存泄漏。典型的技术
听取事件就是使用
语言特定的语法
将处理程序附加到a上的事件
资源。例如,在C#中,那
语法是:source.SomeEvent + = new
SomeEventHandler(一个MyEventHandler)。
这种技术创造了强大
从事件源引用到
事件监听器。通常,附加
监听器的事件处理程序导致
听众有一个对象
受物体影响的一生
来源的生命周期(除非
事件处理程序被明确删除)。
但在某些情况下你可能会
想要对象的生命周期
听众只能被控制
其他因素,如是否
目前属于可视化树
应用程序,而不是
源头的生命周期。每当
源对象的生命周期超出了
监听器的对象生存期,
正常事件模式导致a
内存泄漏:保留了监听器
活得比预期长。
我们将WPF用于具有大型ToolWindows的客户端应用程序,可以拖放,所有漂亮的东西,以及所有兼容的XBAP ..但我们遇到了一些没有垃圾收集的ToolWindows同样的问题..这是应该的事实上它仍然依赖于事件监听器..现在,当您关闭窗口并关闭应用程序时,这可能不是问题。但是如果你用很多命令创建非常大的ToolWindows,并且一遍又一遍地重新评估所有这些命令,人们必须整天使用你的应用程序..我可以告诉你..它真的堵塞了你的记忆和你的应用程序的响应时间..
此外,我发现更容易向我的经理解释我们有内存泄漏,而不是向他解释由于某些需要清理的事件而导致某些对象没有被垃圾收集;)
@Nick是的,附加行为的事情是,根据定义,它们与您正在处理的事件的元素不在同一个对象中。
我认为答案在于以某种方式使用WeakReference,但我没有看到任何简单的代码示例来向我解释。 :)
为了解释我对约翰芬顿的评论,这里是我的答案。让我们看看以下示例:
class Program
{
static void Main(string[] args)
{
var a = new A();
var b = new B();
a.Clicked += b.HandleClicked;
//a.Clicked += B.StaticHandleClicked;
//A.StaticClicked += b.HandleClicked;
var weakA = new WeakReference(a);
var weakB = new WeakReference(b);
a = null;
//b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("a is alive: " + weakA.IsAlive);
Console.WriteLine("b is alive: " + weakB.IsAlive);
Console.ReadKey();
}
}
class A
{
public event EventHandler Clicked;
public static event EventHandler StaticClicked;
}
class B
{
public void HandleClicked(object sender, EventArgs e)
{
}
public static void StaticHandleClicked(object sender, EventArgs e)
{
}
}
如果你有
a.Clicked += b.HandleClicked;
并且只将b设置为null两个引用weakA和weakB保持活着!如果你只设置一个空值b保持活着而不是一个(这证明John Fenton错误地说明硬件引用存储在事件提供者中 - 在这种情况下是a)。
这导致我得出错误的结论
a.Clicked += B.StaticHandleClicked;
会导致泄漏,因为我虽然a的实例将由静态处理程序保存。事实并非如此(测试我的程序)。在静态事件处理程序或事件的情况下,它是相反的方式。如果你写
A.StaticClicked += b.HandleClicked;
参考将保留给b。
确保事件引用元素与它们引用的对象一起使用,就像表单控件中的文本框一样。或者,如果无法阻止。在全局帮助器类上创建静态事件,然后监视事件的全局帮助程序类。如果使用WeakReference无法完成这两个步骤,它们通常适用于这些情况,但它们会带来开销。