[英]How do events cause memory leaks in C# and how do Weak References help mitigate that?
有兩種方法(我知道)在C#中導致無意的內存泄漏:
IDisposable
的資源 我真的不明白第二點。 如果源對象的生命周期比偵聽器長,並且當沒有其他引用時,偵聽器不再需要事件,則使用普通的.NET事件會導致內存泄漏:源對象將偵聽器對象保存在內存中應該是垃圾收集。
你能用C#中的代碼解釋事件如何導致內存泄漏,以及如何使用弱引用和沒有弱引用來編寫代碼來解決它?
當偵聽器將事件偵聽器附加到事件時,源對象將獲得對偵聽器對象的引用。 這意味着在分離事件處理程序或收集源對象之前,垃圾收集器無法收集偵聽器。
考慮以下類:
class Source
{
public event EventHandler SomeEvent;
}
class Listener
{
public Listener(Source source)
{
// attach an event listner; this adds a reference to the
// source_SomeEvent method in this instance to the invocation list
// of SomeEvent in source
source.SomeEvent += new EventHandler(source_SomeEvent);
}
void source_SomeEvent(object sender, EventArgs e)
{
// whatever
}
}
...然后是以下代碼:
Source newSource = new Source();
Listener listener = new Listener(newSource);
listener = null;
即使我們為listener
分配null
,它也不符合垃圾收集的條件,因為newSource
仍然保持對事件處理程序的引用( Listener.source_SomeEvent
)。 要解決此類泄漏,在不再需要事件偵聽器時始終分離事件偵聽器非常重要。
編寫上述示例以關注泄漏問題。 為了修復該代碼,最簡單的方法可能是讓Listener
保持對Source
的引用,以便以后可以分離事件監聽器:
class Listener
{
private Source _source;
public Listener(Source source)
{
_source = source;
// attach an event listner; this adds a reference to the
// source_SomeEvent method in this instance to the invocation list
// of SomeEvent in source
_source.SomeEvent += source_SomeEvent;
}
void source_SomeEvent(object sender, EventArgs e)
{
// whatever
}
public void Close()
{
if (_source != null)
{
// detach event handler
_source.SomeEvent -= source_SomeEvent;
_source = null;
}
}
}
然后調用代碼可以發信號通知它是使用對象完成的,這將刪除Source
對'Listener`的引用;
Source newSource = new Source();
Listener listener = new Listener(newSource);
// use listener
listener.Close();
listener = null;
閱讀Jon Skeet關於事件的精彩文章 。 這不是傳統意義上的真正的“內存泄漏”,而是更多的未被斷開的保持引用。 所以要記住-=
一個事件處理程序,你在前一點+=
並且你應該是金色的。
嚴格來說,在托管的.NET項目的“沙箱”中沒有“內存泄漏”; 只有開發人員認為必要的參考時間才會更長。 弗雷德里克有權利; 當您將處理程序附加到事件時,因為處理程序通常是實例方法(需要實例),所以只要維護此引用,包含偵聽器的類的實例就會保留在內存中。 如果偵聽器實例依次包含對其他類的引用(例如,對包含對象的反向引用),則在偵聽器退出所有其他范圍之后,堆可以保持很長時間。
也許對Delegate和MulticastDelegate有更多深奧知識的人可以對此有所了解。 我看到它的方式,如果滿足以下所有條件,則可能出現真正的泄漏:
我從來沒有聽說過任何涉及在委托目標上調用Dispose()的最佳實踐,更不用說事件監聽器了,所以我只能假設.NET開發人員知道他們在這種情況下做了什么。 如果這是真的,並且事件后面的MulticastDelegate嘗試正確處理偵聽器,那么所有必要的是在需要處理的偵聽類上正確實現IDisposable。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.