2012-02-17 56 views
0

考慮最簡單的COM對象,我們可以在C#中定義的下面的例子創建.NET COM對象實例時,巨大的性能退化(使用Visual Studio 2010 SP1和.NET Framework 4.0中內置):內存泄漏和無GuidAttribute

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 

namespace CcwTestLib 
{ 
    [ComVisible(true)] 
    [Guid("8ABD40E2-05E2-4436-9EAD-073911357155")] 
    public class CcwTestObject 
    { 

    } 
} 

我們編譯這個組裝和使用regasm註冊它的COM互操作(或內置的選項在Visual Studio)。

現在我們只是在寫什麼也不做,除了創建該對象的實例,並釋放10萬次C++非託管的Win32控制檯應用程序。例如使用下面的程序:

#include "stdafx.h" 

// {8ABD40E2-05E2-4436-9EAD-073911357155} 
static const GUID CLSID_CcwTestObject = 
    { 0x8abd40e2, 0x5e2, 0x4436, { 0x9e, 0xad, 0x7, 0x39, 0x11, 0x35, 0x71, 0x55 } }; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    CoInitializeEx(NULL, COINIT_MULTITHREADED); 

    IUnknown *pTestObject = NULL; 

    const int iCount = 100000; 

    wprintf(L"Allocating COM instance %i times...\n", iCount); 

    for (int i = 0; i < iCount; i++) 
    { 
     HRESULT hr = CoCreateInstance(CLSID_CcwTestObject, 
            NULL, 
            CLSCTX_INPROC_SERVER, 
            IID_IUnknown, 
            (LPVOID*)&pTestObject); 
     if (FAILED(hr)) 
     { 
     wprintf(L"Error: %i", hr); 
     return -1; 
     } 

     pTestObject->Release(); 
    } 

    CoUninitialize(); 

    return 0; 
} 

運行時和本地系統上該應用中它大約820ms完成,消耗約32MB內存。將iCount增加到10,000,000使得程序花費更長的時間才能完成(當然),但是查看內存消耗會增加到大約92MB,並且在剩餘的程序執行期間停留在那裏。迄今爲止沒有什麼奇怪的。

現在的有趣的部分,導致我的問題。讓我們從.NET類中刪除Guid屬性(並禁用自動COM註冊(如果啓用,以便先前的註冊在註冊表中保持不變),然後重新構建程序集。

我們與iCount集100,000再次運行測試程序。這次該程序在大約90,000ms完成!大約是比以前慢100倍

更有趣的和麻煩的是,當我們增加iCount〜1000萬,啓動程序。如果我們使用Process Explorer或VMMap或類似的程序監視其內存消耗,我們可以看到它緩慢增加,但它不會像我們預期的那樣停留在92MB。相反,它似乎永遠持續下去。假設虛擬內存空間用盡在2GB左右(因爲它是一個x86進程),應用程序可能會崩潰,但由於速度太慢,我們不會等待這個測試發生,但會退出1,200MB左右。

應該指出的是,使用COM對象,調用它的方法等(如果我們已經定義了任何),它應該很好,因爲它應該是所有必要的信息來創建對象存儲在註冊表中。它在我們的系統的一部分如下所示:

[HKEY_CLASSES_ROOT\CLSID\{8ABD40E2-05E2-4436-9EAD-073911357155}\InprocServer32] 
@="mscoree.dll" 
"ThreadingModel"="Both" 
"Class"="CcwTestLib.CcwTestObject" 
"Assembly"="CcwTestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
"RuntimeVersion"="v4.0.30319" 
"CodeBase"=file:///D:/Coding/Projects/CcwTest/CcwTestLib/bin/Debug/CcwTestLib.dll 

凡CLSID正確地指向大會及其代碼庫,並裝配中顯式類型。

我們還發現,將屬性中的Guid更改爲除註冊之外的任何內容都會造成同樣的問題。

那麼爲什麼會發生這種情況呢?這是.NET中的錯誤嗎?有沒有解決這個問題的方法?

對於這個問題的一些瞭解,我們將非常高興,這個問題花了我們大約一週的時間,從我們的產品中發現的內存泄漏縮小到這個簡化的情況。

+0

當COM調用返回錯誤時,任何正常的代碼停在1。 – 2012-02-18 01:22:30

+0

但是這裏沒有調用會返回錯誤? – DeCaf 2012-02-18 07:10:59

+0

好吧,那很糟糕。創建未註冊類的對象應該總是會產生一個錯誤。你最好找出原因。 – 2012-02-18 12:39:33

回答

0

看來內存泄漏是已被固定在.NET 4.5中的錯誤。

欲瞭解更多詳細信息,請參閱Microsoft Connect問題。

從他們的回答引用:

內存泄漏已經是固定的(.NET框架4.5開發者預覽版是第一個公開可用的構建與修復)。至於緩慢,Guid不匹配會有效地停用我們的內部緩存,因此我們在每次激活時執行一系列註冊表查找和名稱類型查找。由於這不是主線情景,應該僅限於在未更新註冊的情況下重新引導課程的情況,因此我們現在將這個問題放在優先位置。它仍然被追蹤用於未來版本。