2013-06-28 22 views
1

我在尋找管理思想在.NET程序集,然後被傳遞迴非託管代碼創建的COM對象的生存期。對Marshal.ReleaseComObject和返回值

背景: 我們的軟件引擎被編碼在非託管C++。我們的軟件的功能可以使用引擎的COM對象組件進行擴展。多數情況下,發動機本身並不需要改變多年。但是,我們繼續構建更多組件來爲我們的軟件添加新功能。雖然這些組件過去也是使用非託管C++構建的,但在過去的幾年中,我們一直在用C#編寫這些插件。

直到最近,我們已經訂閱了這個想法Marshal.ReleaseComObject不應該在C#組件中使用的非託管COM對象上調用,而是允許RCW和垃圾收集管理它們的生命週期。

這個理論聽起來不錯,但實際上我們已經開始在我們的軟件中遇到內存問題,這似乎是GC懶惰地清理這些COM對象造成的,在某些情況下,我們的軟件會用盡所有可用的記憶和失敗。

有一些錯誤,我們可能會犯被阻止GC和RCW作用於這個對象?垃圾收集的想法不是它會做什麼必要的,以確保內存在需要時是免費的?

由於缺乏更好的想法,我們不情願地開始利用Marshal.ReleaseComObject的(以及在某些情況下FinalReleaseComObject)。一般來說,這看起來確實有用。

然而,在試圖開發使用Marshal.ReleaseComObject的一個統一的模式,我們發現在我們不知道它是可以使用它一個案例。某些組件需要將COM對象返回值傳遞迴非託管引擎。組件再也不會再看到返回值,並且(當前)無法在不再使用時收到通知。例如:

// Unmanaged C++ code 
Engine::Engine() : m_ipDoodadCreator(CLSID_FancyComponent) 
{ 
} 
void Engine::ProcessDoodad() 
{ 
    try 
    { 
     // Smart pointer 
     IDoodadPtr ipDoodad = m_ipDoodadCreator->CreateDoodad(); 
     ... 
     ipDoodad = NULL; // Calls Release... 
    } 
    catch (...) { ... } 
    // At this point the doodad lives on because the RCW is holding a 
    // reference to it. 
} 

// Managed C# Component 
public class FancyComponent : IDoodadCreator 
{ 
    public IDoodad IDoodadCreator.CreateDoodad() 
    { 
     // IDoodad is implmented in unmanaged c++ 
     IDoodad doodad = new IDoodad(); 

     try 
     { 
      ... 

      return doodad; 
     } 
     finally 
     { 
      // Can't do this here because engine doesn't yet have 
      // a reference to doodad. 
      // Marshal.ReleaseComObject(doodad); 
     } 
    } 
} 

到目前爲止,唯一的想法,我們可以拿出以確定性得到RCW釋放其在這種情況下引用的將是重新工作的引擎,使所有的接口都有一個不時調用Cleanup()調用,但這會很耗時,並且在某些情況下,實現起來非常麻煩。

tl; dr有沒有辦法強制RCW釋放其對正在返回到非託管環境的COM對象的引用?

+0

'IDoodad'應該繼承'IUnknown',所以你應該能夠通常調用'Release'最後我檢查 – Mgetz

+0

發佈遞減引用計數。如果其他東西(RCW)仍然有參考價值,那麼塗鴉就會繼續存在。在上面的代碼中,我暗示IDoodadPtr是一個智能指針,它在超出範圍時調用Release。 – Alias

+0

你的意思是[COM Callable Wrapper](http://msdn.microsoft.com/en-us/library/f07c8z1c.aspx)?根據MSDN,如果正常調用發行版,它將釋放垃圾收集對象。 – Mgetz

回答

-1

我們當前問題的答案是GC.AddMemoryPressure。

由於我們通過未管理類的RCW實例創建和引用,垃圾收集器並不知道爲這些對象分配的內存。 按照MSDN幫助這個方法:

在確定何時安排垃圾收集,運行時會考慮很多管理內存是如何分配的。如果一個小的託管對象分配了大量的非託管內存,運行時將僅考慮託管內存,因此低估了調度垃圾回收的緊迫性。

http://msdn.microsoft.com/en-us/library/system.gc.addmemorypressure.aspx

換句話說,這並不是說我們需要明確釋放COM對象......我們只需要告訴GC多少內存與它們相關聯。事實上,在某些關鍵地方實施此方法後,垃圾收集器的表現會更好。

+0

我不經常發帖,但它似乎是一個很好的禮儀,添加註釋解釋爲什麼你倒了一個答案,所以作者可以理解什麼是不正確的/無益的... – Alias

1

但在實踐中,我們已經開始有我們的軟件

這是一個典型的成長痛問題內存問題。軟件永遠不會變小,團隊中沒有人因寫負號而獲獎。添加越來越多的功能,你的程序永遠不會使用內存。標準的診斷是消耗第一個千兆字節的虛擬內存空間需要很長時間。第二個千兆字節很快就消失了。

你現在在哪裏是非生產性的,你正在煩惱幾兆字節。只需將開關撥至非問題即可。將本地代碼編譯爲64位。你不會得到從託管代碼的任何異議,64抖動已經知道該怎麼做,沒有任何變化

+0

我認爲你是對的,我們需要尋找其他策略。我們不久前使用的一個是打開大地址感知標誌。但是,儘管x64可能不是「非生產性」的,因爲我們可以從中獲得長期利益,但它並不容易,也不是我們可以花時間處理眼前的問題;我們有很多遺留的C++代碼,其中類型的選擇並沒有考慮到最終轉移到x64 ... – Alias