2010-04-29 45 views
14

我最近在WinForms應用程序中查看了一些.NET「內存泄漏」(即意外的,滯留的GC根源對象)。在加載並關閉一個巨大的報告之後,即使在幾個gen2集合之後,內存使用率也沒有像預期的那樣下降。假設報告控件被一個流浪事件處理程序保持生存,我打開WinDbg以查看發生了什麼.......NET正則表達式「內存泄漏」調查

使用WinDbg,!dumpheap -stat命令報告字符串實例消耗了大量內存。通過!dumpheap -type System.String命令進一步細化,我找到了罪魁禍首,在地址爲03be7930的報告中使用了一個90MB的字符串。最後一步是調用!gcroot 03be7930來查看哪個對象保持活着狀態。

我的期望不正確 - 它不是懸掛在報告控件(和報告字符串)上的未掛鉤事件處理程序,而是它由System.Text.RegularExpressions.RegexInterpreter實例保留,該實例本身是System.Text.RegularExpressions.CachedCodeEntry的後代。現在,正則表達式的緩存(有點)是常識,因爲這有助於減少每次使用時重新編譯正則表達式的開銷。但是,這與保持我的字符串活着有關呢?

基於使用Reflector進行分析,事實證明,每當調用Regex方法時,輸入字符串都存儲在RegexInterpreter中。 RegexInterpreter持有這個字符串引用,直到通過隨後的Regex方法調用將一個新的字符串提供給它爲止。我期望通過掛在Regex.Match實例以及其他可能的實例上來實現類似的行爲。該鏈是這樣的:

  • Regex.Split,Regex.Match,Regex.Replace等
    • Regex.Run
      • RegexScanner.Scan(RegexScanner是基類,RegexInterpreter是上述的子類)。

違規的正則表達式是僅用於報告,很少使用,因此不太可能被再次使用,以清除現有的報表字符串。即使後來使用了正則表達式,它也可能會處理另一個大的報告。這是一個相對重要的問題,只是普通的感覺很髒。

說了這麼多,我發現了一些關於如何解決這個問題,或者至少解決這個問題的方案。我會讓社區先回應,如果沒有參與者出面,我會在一兩天內填補任何空白。

+1

是否使用在創建正則表達式的'Compiled'選項? – 2010-04-29 16:31:00

+0

沒有,是不是在這種情況下使用的'Compiled'選項。 – 2010-04-30 00:45:03

回答

8

您是否使用正則表達式的實例或採用字符串模式的靜態正則表達式方法? According to this post,正則表達式實例不參與緩存。

+2

是的,靜態正則表達式方法的使用是罪魁禍首。您可以通過Reflector驗證靜態方法是否使用了緩存 - 所有靜態調用都使用採用'useCache'參數的私有ctor創建Regex。 這裏的簡單的解決辦法是不使用靜態方法。緩存並不重要,因爲與處理巨大的輸入字符串相比,編譯是微不足道的。其他可能有用的解決方案(取決於使用正則表達式的方式)是通過將Regex.CacheSize設置爲0或在處理源後通過正則表達式運行一個空字符串來禁用Regex緩存。 – 2010-04-30 00:52:11

0

嘗試切換到一個編譯正則表達式 - 實例需要更長的時間,但也許不會受到這種奇怪的泄漏。

請參閱http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regexoptions%28v=VS.100%29.aspx瞭解更多信息。

或者,不守住正則表達式實例的時間比你需要 - 創建一個新的一個每個報告調用。

+2

Downvoted,因爲[編譯正則表達式總是會泄漏內存](按照設計)(Assemblies不可卸載,除非卸載整個AppDomain):然而,應該提到的編譯成本更高。用反射發射IL.Emit加載了大量的代碼並使用了大量的內存,這並不是你將要回頭的內存。「除非你在一次性的AppDomain中做這件事,這帶來了很多新的挑戰,例如與應用程序內域性能相比,例如差的AppDomain性能。 – 2015-06-25 22:30:17