當前位置: 妍妍網 > 碼農

【譯】解開托管記憶體的秘密:深入了解事件處理常式泄漏!

2024-01-27碼農

事件處理常式泄漏已經存在很長時間了,這是 WPF (Windows Presentation Foundation)開發人員經常要處理的最麻煩的問題之一。您可能會想:是什麽讓事件處理常式泄漏如此重要?事件處理常式泄漏很容易引起,只需忘記取消訂閱事件即可。此外,它們很難發現,甚至更難修復。

在更新17.9預覽1中引入的 Visual Studio 托管記憶體使用工具中添加的新見解大大簡化了發現/修復這些泄漏的過程。它提供有關哪些物件正在泄漏及其訂閱的事件的資訊。

什麽是事件處理常式泄漏?

當物件在功能上無法使用時,沒有被辨識為垃圾收集時,它會在堆上泄漏。這意味著它將無意中在記憶體中保持活動狀態。事件處理常式因導致這種情況而臭名昭著。這是因為事件處理常式在物件和它訂閱的事件之間建立了一個直接參照。

在本例中,我們有一個 Publisher 和 Subscriber 類。當 Subscriber 呼叫 Subscribe 時,MyEvent 將在堆中連結 Publisher 和 Subscriber:

這樣做的問題是,如果 Subscriber 忘記取消訂閱,這些參照將保留在堆 上,從而泄漏 Subscriber。 這裏最簡單的解決方案是簡單地呼叫 Unsubscribe 方法,但是在更復雜的應用程式中,很難跟蹤物件的訂閱以及何時退訂。 這就是 Memory Analyzer 可以幫助開發人員解決這些問題的地方。

好吧,讓我們看看如何解決它?

為了證明這一點,我們將對一個範例 WPF 應用程式進行演練偵錯,以尋找事件處理常式泄漏:

在本例中,我們有一個視窗,它在開啟時訂閱 dispatcherTimer_Tick 事件。事件在做什麽並不重要。這段程式碼的重要部份是,當視窗關閉時,我們忘記取消訂閱事件:

這裏取消訂閱語句的註釋是有問題的,因為它在視窗關閉時不再正確地取消訂閱,並且會導致泄漏。為了找到它,讓我們開始偵錯這個應用程式(F5)。為簡單起見,讓我們假設 main() 正確地導致泄漏,它開啟 AdWindow,導致它訂閱一個事件,然後關閉它。

首先,我們需要開啟診斷工具視窗。要在偵錯會話中存取它,請執行 Debug -> Windows -> Diagnostic Tools。

它在偵錯會話中開啟時,它應該是這樣的:

此視窗顯示正在偵錯的應用程式的堆的總體大小和 CPU 百分比。 當我們單擊相機圖示拍攝快照時,我們可以透過單擊 Objects 下的值來檢視堆並存取事件處理常式泄漏見解。

進入快照的堆檢視後,導航到 Insights(見解):

我們終於到站了!在 Insights 索引標籤中,我們可以看到泄漏事件處理常式的列表,並且我們可以看到泄漏視窗的顯示。此外,我們可以看到該泄漏所浪費的總量。僅僅是這個簡單的例子就造成了 4.93 KB 的泄漏!這是因為視窗有一個完整的子樹,它參照的物件也會因為忘記取消訂閱而泄漏。

此外,您可以透過點選「Show Just My code」來過濾掉所有的系統程式碼,只顯示 AdWindow。

這很酷, 但現在怎麽辦?

到目前為 止,我們已經成功地確定了我們的應用程式中的泄漏。 現在,如果我們想要修復它,我們可以點選「View Details」來檢視有關問題的更多資訊,更重要的是,如何修復它。

這個檢視向我們展示了一些關於泄漏的關鍵資訊。 我們可以看到物件的地址,它持有的事件處理常式,最重要的是,它訂閱的物件。 這傳達了要解決這個問題,AdWindow 必須從 DispatcherTimer 取消訂閱。 此外,您還可以看到 AdWindow 物件的參照圖。 「Referenced Objects」索引標籤顯示了由於 AdWindow 而泄漏的額外物件的數量。

我們還能做什麽?

如果您已經讀到這裏了,你可能會對這種見解的其他用途感興趣。例如,檢測邏輯可以處理任何型別的事件處理常式。考慮前面的帶有釋出者和訂閱者類的控制台應用程式,如果我們建立自己的事件處理常式,我們仍然可以檢測到它。

告訴我們您的想法!

在未來的版本中,這種體驗還會有很多改進。請下載最新的 Visual Studio 預覽版並提供您的反饋。請在 Visual Studio 中透過「Report a Problem」或直接在開發者社群站點提出問題並提供反饋。

原文連結:https://devblogs.microsoft.com/visualstudio/unlocking-the-secrets-of-managed-memory-dive-into-event-handler-leak-insights/