2017-04-05 76 views
10

在生產中安裝我的Windows服務之前,我正在尋找可執行的可靠測試,以確保我的代碼不包含內存泄漏。然而,我在網上找到的所有東西都是使用任務管理器來查看使用過的內存或一些付費的內存分析器工具。追蹤.NET Windows服務內存泄漏

從我的理解來看,任務管理器並沒有真正的幫助,也無法確認內存泄漏(如果存在)。

  1. 如何確認是否有內存泄漏?

  2. 是否有任何免費工具來查找內存泄漏的來源?

注:我使用.NET Framework 4.6和Visual Studio 2015年社區

+0

這一切都取決於你的服務究竟做了什麼。 – Evk

+0

@Evk無論服務如何,我如何確保服務中沒有內存泄漏? – Mhd

+0

我的意思是很難真正測試內存泄漏的程序,特別是Windows服務。在一段時間內,你必須廣泛地使用它。增長的記憶本身並不表示泄漏,因爲如果沒有理由(無記憶壓力),GC可能決定不收集任何東西。所以你可以做的最好的是編寫好的代碼而不會泄漏,並且隨着時間的推移監視應用程序的內存使用情況,如果內存達到某個閾值並收集分析器,則收集內存轉儲。 – Evk

回答

7

那麼你可以使用任務管理器。 GC應用程序可能會泄漏內存,並會顯示在那裏。

但是......

免費工具 - 「.NET CLR仿形」

有一個免費的工具,它從微軟和它的真棒。對於所有泄漏引用的程序來說,這是必須使用的。搜索MS'網站。

泄漏引用意味着您忘記將對象引用設置爲null,或者他們永遠不會留下作用域,這在垃圾收集語言中幾乎可能發生爲非 - 建立和未清除列表,指向代表的事件處理程序,等等

這是內存泄漏的GC等價物,具有相同的結果。這個程序告訴你哪些參考文件佔用了大量的內存 - 並且你會知道它是否應該是這樣或者沒有,如果不是,你可以去找到它們並解決問題!

它甚至有一個很酷的可視化對象分配什麼內存(所以你可以追查錯誤)。如果你需要解釋,我相信你有這種情況。

Memory Usage Visualization

Wikipedia page with download links...

注意:您將有可能運行你的應用程序爲使用該服務。它首先啓動,然後運行你的應用程序。您可以使用TopShelf或者將膽量放入一個從EXE運行的dll中運行,這個dll包含了服務集成(服務主機模式)。

0

除非你正在處理與非託管代碼,我會如此大膽的說出你不必擔心內存泄漏。託管代碼中的任何未引用的對象將被垃圾收集器刪除,並且在.net框架內發現內存泄漏的可能性我會說你應該被認爲是非常幸運的(好,不幸)。你不必擔心內存泄漏。

但是,如果對對象的引用永遠不會釋放,您仍然可能遇到不斷增長的內存使用情況。例如,假設你保持一個內部日誌結構,並且只是不斷地向日志列表添加條目。然後每個條目仍然有來自日誌列表的引用,因此將永遠不會收集。

根據我的經驗,您絕對可以使用任務管理器作爲您的系統是否存在增長問題的指標;如果內存使用量穩步上升,則說明您有問題。如果它增長到一個點,但最終收斂到一定的規模,則表明它已達到其操作閾值。

如果您想要更詳細地瞭解託管內存使用情況,可以下載由Microsoft開發的進程管理器here。它仍然非常直截了當,但它比任務管理器提供了更好的統計視圖。

+0

這是一個很好的答案。它回答你的第一個問題:你可以使用任務管理器來觀察內存使用行爲。如果內存使用量永遠不會下降,則表明您有問題。從我的經驗來看,這很有效。不需要其他工具。關鍵是垃圾收集器只能刪除未引用的對象,因此確保在完成時所有對象的引用都被銷燬。在整個服務中實現Dispose模式。調用Dispose(可用)並將大對象設置爲null。這是防止內存問題的最佳方法。 –

+0

@米卡爾事實並非如此。內存泄漏也可能由託管代碼造成。認爲沒有處置的對象,委託訂閱,弱引用... – Mhd

+0

未處理的對象最終將由垃圾收集器處置。我想這歸結於定義。內存泄漏通常是指沒有被釋放的內存片段,但沒有任何內容泄漏。每個塊引用另一塊的內存塊鏈不被認爲是內存泄漏,而是一個效率低下的結構;你不會希望垃圾收集器釋放內存,因爲你實際上可能有理由保持這種方式。這是一種記憶食物,但它不是內存泄漏。 – Micael

0

我不會說垃圾收集器是無誤的。有時候它在不知不覺中失敗了,他們並不那麼直截了當。內存流是內存泄漏的常見原因。您可以在一個上下文中打開它們,即使用法被包裝在using語句中(即在使用超出範圍後應立即清理的可丟棄對象的定義),它們也可能永遠不會關閉。如果由於內存不足而發生崩潰,Windows會創建可以篩選的轉儲文件。

enter link description here

這絕不有趣或容易,是相當乏味,但它往往是你最好的選擇。

容易產生內存泄漏的公共區域是任何使用System.Drawing dll,內存流,以及是否正在執行一些嚴重的多線程的東西。

3

雖然託管代碼意味着沒有直接內存管理,但您仍然需要管理您的實例。這些實例'索賠'的記憶。這些都是關於這些實例的用法,當你不指望它們成爲時,讓它們活着。

只是衆多例子中的一個:一次性類的錯誤使用可能會導致很多實例聲明內存。對於Windows服務,實例緩慢而穩定的增加可能最終導致大量內存使用量。

是的,有一個工具可以分析內存泄漏。它只是不自由。但是,您可能能夠在7天的路線中識別您的問題。

我建議在.NET Memory Profiler採取戰利品。

在開發過程中分析內存泄漏非常好。它使用快照的概念來比較新的實例,處置的實例等。這對了解服務如何使用其內存很有幫助。然後,您可以深入瞭解爲什麼創建新實例或保持活躍狀態​​。

是的,您可以測試以確認是否引入了內存泄漏。 但是,只是開箱即用,這不會很有用。這是因爲沒有人能預測運行時會發生什麼。該工具可以分析您的應用程序的常見問題,但這不能保證。

但是,您可以使用這個工具的內存消耗融入像NUnitMSTest您的單元測試框架。

0

如果你使用實體框架和一個DI模式,可能使用Castle Windsor,你可以很容易地得到內存泄漏。

要做的主要事情是使用using(){}語句,您可以在其中自動將對象標記爲已處理。

另外,您希望關閉您只在閱讀而不在寫作的實體框架上的自動跟蹤。最好隔離你的寫入,在這一點上使用using(){},獲取一個dbContext(跟蹤),寫入你的數據。

如果你想調查什麼是在堆上。我使用的最好的工具是RedGate ANTS http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/solving-memory-problems/getting-started不便宜,但它的工作原理。

但是,通過使用using(){}模式,您可以(不要創建靜態或單例DbContext,也不會在大量更新循環中擁有一個上下文,請儘可能多地處理它們!),那麼你會發現內存通常不是問題。

希望這會有所幫助。

2

當然,內存分析器是第一種嘗試的工具,但它只會告訴你實例是否在不斷增加。你仍然想知道它們是否正在增加是正常的。另外,一旦你確定有些情況因爲沒有正當理由而持續增加(意思是說,你有泄漏),你會想知道哪些調用樹會導致它們的分配,這樣你就可以解決分配它們的代碼,修復它,以便它最終釋放它們。

下面是一些我在處理這些問題多年來收集的知識:

  1. 測試服務作爲常規可執行文件儘可能。試圖將服務作爲實際服務來測試只會讓事情變得過於複雜。

  2. 養成你在做的事情範圍的末尾明確撤銷你所做的一切的習慣。例如,如果你向某個觀察者的事件註冊觀察者,應該總是有某個時間點(處置觀察者或觀察者?),以便取消註冊。從理論上講,垃圾回收應該通過收集整個相互關聯的觀察者和觀察者的圖表來處理,但是在實踐中,如果你不習慣忘記撤銷你所做的事情,那麼你會得到內存泄漏。

  3. 儘可能地使用IDisposable,並且如果有人忘記調用Dispose()就會讓你的析構函數報告。關於此方法的更多信息,請參閱:Mandatory disposal vs. the "Dispose-disposing" abomination披露:我是該文章的作者。

  4. 在您的程序中定期檢查點,您釋放應該可釋放的所有內容(就像程序正在執行有序關閉以終止一樣),然後強制垃圾回收以查看是否有任何泄漏。

  5. 如果某個類的實例看起來似乎在泄漏,請使用以下技巧來發現導致其分配的精確調用樹:在該類的構造函數內分配一個異常對象而不拋出它,獲取堆棧跟蹤例外,並存儲它。如果您稍後發現此對象已泄漏,則您有必要的堆棧跟蹤。只是不要用太多的對象來做這件事,因爲分配一個異常並從中獲取堆棧跟蹤速度非常慢,只有微軟知道爲什麼。

1

你可以嘗試免費的存儲管式示波器內存分析器

https://github.com/fremag/MemoScope.Net

我不同意,你可以信任的任務管理器中查看是否有內存泄漏與否。垃圾收集器的問題在於它可以基於啓發式來決定在內存峯值之後保留內存,並且不會將其返回給操作系統。您可能有2 GB的提交大小,但其中90%可以免費。

您應該使用VMMAP在測試過程中檢查您的進程包含的內存類型。你不僅擁有託管堆,還有非託管堆,私有字節,堆棧(線程泄漏),共享文件等等,這些都需要跟蹤。

VMMap還有命令行界面,可以定期創建快照,以後可以檢查。如果你有一個內存增長,你可以找出哪種類型的內存泄漏哪些需要取決於泄漏類型不同的調試工具的方法。