【发布时间】:2011-12-31 11:26:26
【问题描述】:
我有一个 C# COM DLL,它使用 System.Management 命名空间调用 WMI。 DLL 正在加载到 C++ 服务中。每次我调用 WMI 类时,我都会看到巨大的内存泄漏。大约一个小时后,我已经使用了超过 1 GB 的内存。
如果我使用相同的 COM DLL 并使用 Reflection.LoadFrom 将其加载到 PowerShell 中,它不会泄漏内存。我已经像这样修改了 DLL,它不再泄漏(仍然使用 COM 加载到服务中):
public class MyComObject
{
public void CallCom()
{
CallSomeWMIStuff();
}
}
到这个。这不再泄漏!
public class MyComObject
{
public void CallCom()
{
//CallSomeWMIStuff();
}
}
这是一些 WMI 代码的示例:
var scope = new ManagementScope( "root\\cimv2" );
scope.Connect();
using (var myservice = GetService("SomeService", scope))
{
//Some Stuff
}
...
ManagementObject GetService(string serviceName, MangementScope scope)
{
ManagementPath wmiPath = new ManagementPath( serviceName );
using (ManagementClass serviceClass = new ManagementClass( scope, wmiPath, null ))
{
using (ManagementObjectCollection services = serviceClass.GetInstances())
{
ManagementObject serviceObject = null;
// If this service class does not have an instance, create one.
if (services.Count == 0)
{
serviceObject = serviceClass.CreateInstance();
}
else
{
foreach (ManagementObject service in services)
{
serviceObject = service;
break;
}
}
return serviceObject;
}
}
}
编辑:C++ 片段:
NAMESPACE::ICSharpComPtr pCSharpCom = NULL;
HRESULT hr = pCSharpCom .CreateInstance(NAMESPACE::CLSID_CSharpCom);
if (FAILED(hr))
{
Log("Failed (hr=%08x)", hr);
return hr;
}
try
{
_bstr_t bstrData = pCSharpCom ->GetData();
strLine = (LPCTSTR)bstrData;
strMessage += strLine;
}
catch (_com_error& err)
{
_bstr_t desc = GetErrorMessage(err);
Log("Excepton %S", (const wchar_t*)desc);
return 0;
}
pCSharpCom ->Release();
有人见过这样的吗?我们看到与 C++\CLI 类似的问题,它直接加载不同的 WMI 相关 DLL。
最终,WMI 服务将不再响应,我也必须重新启动该服务。
编辑:
这与 COM 对象的单元状态有关。添加了CoInitializeEx 而不是CoInitialize。我将线程设置为 MTA。起初它看起来好像不起作用,直到我意识到第一次调用该方法时,我们看到线程状态设置为 STA 而不是 MTA!随后的每个呼叫都是 MTA。如果我立即返回,在线程为 STA 时调用 System.Management 类之前,我将不再泄漏内存!
知道为什么第一个是 STA 吗?
【问题讨论】:
-
这发生在垃圾收集器不运行、RCW 不被清理时。你可能只是没有产生足够的垃圾。使用 perfmon.exe 进行检查。 GC.Collect 是解决方法。
-
我也试过这个。它似乎没有帮助。
-
经过进一步调查后,GC.Collect 对这种情况没有帮助。它的内存和句柄仍在增长。
-
C++ ICSharpComPtr 类型是如何生成的?还要从发布调用中捕获返回值。如果它的值 > 0 则表明存在对 COM 对象的另一个引用。返回的 bstrData 作为托管类型,如果它的引用没有被正确清理,这可能会导致您的整个 .Net Com 对象永远不会被收集。但是,它在这里是一个字符串,所以我认为它会被当作副本而不是直接引用托管内存。
-
我没有生成它,但是 COM DLL 有一个 TLB。我假设 regasm 或通过构建。发布返回 0。我现在还注意到线程正在堆积...
标签: c# c++ com memory-leaks wmi