考慮最簡單的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中的錯誤嗎?有沒有解決這個問題的方法?
對於這個問題的一些瞭解,我們將非常高興,這個問題花了我們大約一週的時間,從我們的產品中發現的內存泄漏縮小到這個簡化的情況。
當COM調用返回錯誤時,任何正常的代碼停在1。 – 2012-02-18 01:22:30
但是這裏沒有調用會返回錯誤? – DeCaf 2012-02-18 07:10:59
好吧,那很糟糕。創建未註冊類的對象應該總是會產生一個錯誤。你最好找出原因。 – 2012-02-18 12:39:33