2011-03-15 52 views
2

我有一個最初用VB6編寫的應用程序,我使用了一個工具從功能角度轉換爲C#,並取得了相當不錯的成功。它使用大量小到中等大小的COM(C++)對象處理大量消息。有很多COM對象的C#中過多的內存使用

我注意到,在運行使用少於40M內存的舊VB6應用程序中運行的特定測試需要將近900M的C#應用​​程序。如果我將一個GC.Collect()放在C#應用程序的最內層消息處理循環中,它將使用與VB6應用程序相同或更少的內存,儘管它真的很慢。這使我相信絕對沒有「泄漏」這個詞。

然後我通過AQTime內存分析器運行C#應用程序,它報告堆中存在過多數量的COM/C++對象。我假設這是因爲COM對象周圍的運行時可調用包裝非常小,並且從不(或很少)在C#中觸發集合,即使它們引用的COM對象大得多。我想我可以通過在C#應用程序的COM對象中添加顯式的Marshal.ReleaseComObject()調用來解決這個問題。我去了很多地方,在COM對象的生命週期很容易確定的地方。我注意到內存使用量只有非常輕微的下降。

我想知道爲什麼我沒有比這更好的成功。通過查看Marshal類中的靜態方法,我看到一些讓我相信,或許我可能在處理COM引用時丟失了一些微妙之處,或者我認爲RCW的引用計數到達時它們被立即銷燬的假設是不正確的。

我希望對其他方法有任何建議,我可以嘗試或我可能忽略或誤解的其他方法。

+0

如果你不這樣做GC.Collect的,會發生什麼?它會崩潰嗎? – 2011-03-15 02:12:28

+0

如果我刪除GC.Collect()調用,內存使用量大約爲900M。它從來沒有崩潰,但當使用相同的COM對象時,舊的VB6應用程序使用少於40M的內存使用情況是不可接受的。 – Dan 2011-03-15 02:45:06

+0

@Dan:它是_virtual_內存,不是物理內存。過度的分頁會比「使用」的總內存更好。 – 2011-03-15 02:47:19

回答

3

對不起,對於鏈接而不是一個好的簡介,但我從來沒有這個問題自己,因爲我已經處理了長期居住的情況下的IE和mshtml。

物品的狀態:

當使用從基於.NET的應用一個COM對象,但它牽涉到兩個對象:在RCW和COM對象(或對象)。垃圾收集只知道RCW的大小(可能很小),而不是COM對象(可能很大)。因此,儘管基於.NET的應用程序可能會釋放RCW,但即使內存用完,垃圾回收也可能無法回收RCW。只要RCW保留在內存中,它所管理的COM對象也保留在內存中。

有兩種機制確保COM對象從內存中釋放:AppDomain對象和ReleaseComObject方法。使用AppDomain爲管理COM對象提供了最簡單的解決方案,但具有性能成本並可能會帶來安全風險。使用ReleaseComObject可以避免這些成本,但需要更仔細的計劃和編碼。

+0

謝謝,我不知道AppDomain選項,但這可能不適合我的情況,因爲性能在內部消息處理循環中很重要。 – Dan 2011-03-15 02:43:37

+0

@Dan - 我以爲你的循環創建並釋放了很多COM對象。如果是這樣,那本身並不是一件非常有表現力的事情。我推測ReleaseComObject是一個值得實施和分析的非常好的選擇。 – 2011-03-15 03:01:34