【问题标题】:ManagementObject leaking in C# COM DLLC# COM DLL 中的 ManagementObject 泄漏
【发布时间】: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


【解决方案1】:

RCW 实现中没有 dispose,因此您可以任由 GC 来释放您默认创建的 com 对象。但是,您可以在使用完 COM 对象后尝试在 RCW 实例上使用Marshal.FinalReleaseComObject。这将强制包装的 COM 对象上的引用计数为零,它应该释放。但是,这也使 RCW 实例无用,因此在调用它时要小心。

【讨论】:

  • 我会试试你的建议。我每次都在强制 GC.Collect,它似乎正在工作,但它似乎真的很重。
  • Marshal.FinalReleaseComObject 不适用于 System.Management 类。 :|
  • 不起作用,因为它会引发异常,或者它不会释放内存?
  • 抱歉,抛出异常说明对象需要从 __ComObject 派生。
  • 对我的误解深表歉意。我虽然您使用的是 RCW 的 WMI。我现在看到您正在使用 System.Management 命名空间,它确实实现了 dispose 并且您正在调用它。我现在怀疑它可能是 C++ 代码,你能发布你用来创建和调用你的 .Net com 对象的 C++ 代码片段吗?
【解决方案2】:

问题与创建 COM 对象的线程的单元状态有关。有一个线程将 COM 对象创建为 MTA,而另一个线程将 COM 对象创建为 STA。首先创建 STA 线程,然后导致 MTA 线程出现问题。这导致终结器阻塞 GetToSTA。

【讨论】:

    猜你喜欢
    • 2019-09-07
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 1970-01-01
    • 2012-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多