2010-09-24 26 views
3

我有一個C#服務,它監聽隊列中的XML消息,接收它們,使用XSLT處理它們並將它們寫入數據庫。 它每天處理約60K條消息,每條約1Mb。內存閒置時會降到100MB,這真的很不錯。 但是最近我開始處理大小爲12 MB的消息。它確實打擊了內存,即使閒置它也有大約500MB的內存。任何建議,爲什麼這可能是一個問題?我不認爲存在內存泄漏,因爲在處理這麼多的消息(1K的60K消息)後會出現內存泄漏。.NET中的內存問題

+1

當服務空閒併發生垃圾收集時,我期望內存完全停止運行。而且,如果這些巨大的消息中有很多來自服務,它確實會拋出內存異常。 – koumides 2010-09-24 12:47:08

+1

@koumides:一旦你的服務空閒,你的內存將不會被釋放,垃圾收集器會根據所做的分配在最佳時間執行內存釋放。 – James 2010-09-24 12:52:22

+1

你如何測量內存使用量?任務管理器並不是最好的方式...像CLR Profiler這樣的分析器可以讓您更好地瞭解有多少內存正在被分配和使用。 – 2010-09-24 12:53:35

回答

7

看起來非常好。你爲什麼認爲這是一個問題?

垃圾收集器最終會釋放未使用的內存,但這並不意味着一旦您的服務空閒就會發生這種情況。

雷蒙德陳寫了一篇很好的文章解釋了垃圾收集的基本思想:

Everybody thinks about garbage collection the wrong way

但是 - 但是這是純粹的投機,在你的問題中給出的信息 - 有可能是與XSLT中擴展方法相關的內存泄漏。每次新的XML文檔轉換時,擴展方法可能會導致出現問題,以防重新編譯樣式表。修復很簡單:編譯後,緩存樣式表。

1

很難說什麼可能是問題。調查記憶問題時,我已經成功使用Ants Memory Profiler

1

與此想法相反我不認爲有內存泄漏;我會profile的內存。

3

你對內存使用了什麼措施?有很多可能的措施,沒有一個真正意味着「使用過的記憶」。藉助虛擬內存,共享圖像等等,事情並不簡單。

處於空閒狀態,即使它擁有大約500MB

大多數進程的內存(我認爲這包括.NET)不會釋放內存回一次分配過程中的OS。但是如果它沒有被使用,它將從物理內存中分頁,從而允許其他進程使用它。

開始在尺寸

處理的12 MB的消息如果XML文檔擴展成一個對象模型(例如XmlDocument)它需要更多的存儲器(大量的鏈接到其他節點)。要麼查看不同的模型(XDocumentXPathDocument都較輕),或者更好地,使用XmlReader來處理XML,因此從未擁有完全擴展的對象模型。

5

SIR!放下任務經理和行走。認真。 .NET中的內存管理並未針對最小尺寸進行調整。它調整效率。它將保持內存而不是釋放回系統。抵制保姆的誘惑,除非存在實際問題(OOM例外,系統問題)。

2

首先,向系統中添加一些東西,以便您可以手動調用垃圾回收來進行調查。除非供應不足,否則CLR不一定會將內存返回給操作系統。當系統處於靜止狀態時,您應該使用此機制手動調用垃圾回收,以便您可以查看內存是否回退。 (你應該調用GC.Collect(2)兩次,以確保與終結的對象實際上是收集並不僅僅是完成。)

如果內存下降到一個完整的GC後的靜態水平,那麼你沒有一個問題:僅僅是.NET是不積極主動去分配內存。

如果內存不下來,那麼你有某種泄漏。由於你的信息很大,這意味着它們的文本表示很可能以大對象堆(LOH)結束。這個特殊的堆沒有壓縮,這意味着比其他堆更容易泄漏這個內存。

要注意的一件事是字符串實習:如果您手動實習字符串,可能會導致LOH分段。

+0

字符串文字的編譯器實習會導致相同的碎片嗎? – 2010-09-24 13:13:14

+0

@Matt Briggs:不,編譯器實際上並沒有執行任何實際操作,而是所有字符串文字都是以這樣的方式編譯的,即程序啓動時只會創建一個字符串對象。 – 2010-09-24 13:21:16

0

我會檢查你的事件處理程序。如果在完成時不小心分離那些,那麼創建不會被GC收集的對象引用似乎很容易。
我不知道這是最好的做法(因爲開始和取消事件處理的責任應歸於訂閱者),但我已經走過了實現IDisposable並通過顯式委託字段實例創建事件的路線。在處置時,該字段可以設置爲空,從而有效地分離所有訂閱。

是這樣的:

public class Publisher 
    : IDisposable 
{ 
    private EventHandler _somethingHappened; 
    public event EventHandler SomethingHappened 
    { 
     add { _somethingHappened += value; } 
     remove { _somethingHappened -= value; } 
    } 
    protected void OnSomethingHappened(object sender, EventArgs e) 
    { 
     if (_somethingHappened != null) 
      _somethingHappened(sender, e); 
    } 

    public void Dispose() 
    { 
     _somethingHappened = null; 
    } 
} 

除非是(我不知道你有多大的控制了發佈者),你可能需要小心拆下你的用戶不需要的處理程序:

public class Subscriber 
    : IDisposable 
{ 
    private Publisher _publisher; 
    public Publisher Publisher 
    { 
     get { return _publisher; } 
     set { 
      // Detach from the old reference 
      DetachEvents(_publisher); 
      _publisher = value; 
      // Attach to the new 
      AttachEvents(_publisher); 
     } 
    } 

    private void DetachEvents(Publisher publisher) 
    { 
     if (publisher != null) 
     { 
      publisher.SomethingHappened -= new EventHandler(publisher_SomethingHappened); 
     } 
    } 
    private void AttachEvents(Publisher publisher) 
    { 
     if (publisher != null) 
     { 
      publisher.SomethingHappened += new EventHandler(publisher_SomethingHappened); 
     } 
    } 

    void publisher_SomethingHappened(object sender, EventArgs e) 
    { 
     // DO STUFF 
    } 

    public void Dispose() 
    { 
     // Detach from reference 
     DetachEvents(Publisher); 
    } 
}