You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CcomObject内存泄漏排查:UMDH定位的非托管代码泄漏问题

内存泄漏原因分析与验证建议

核心泄漏点定位

通过UMDH捕获的栈信息,泄漏根源指向CMyClass::GetTimeoutObject方法中创建的CComObject<CCoreTimeoutObject>实例未被正确释放。先看该方法的代码逻辑:

STDMETHODIMP CMyClass::GetTimeoutObject(/* [out] */ IMyInterface **ppTimeoutObj)
{
    ATLASSERT(ppTimeoutObj && !(*ppTimeoutObj));

    CComObject<CCoreTimeoutObject> *pTimeoutObj = NULL;
    HRESULT hr = CComObject<CCoreTimeoutObject>::CreateInstance(&pTimeoutObj);
    if (SUCCEEDED(hr))
    {
        *ppTimeoutObj = static_cast<IMyInterface *>(pTimeoutObj);
        (*ppTimeoutObj)->AddRef();
    }

    return hr;
}

这里的关键问题:CComObject::CreateInstance会自动给对象加一次引用(ATL内部逻辑),而代码里又手动调用了AddRef(),导致最终该对象的引用计数多了1次。如果调用方没有正确调用对应次数的Release(),就会直接引发内存泄漏。

托管代码调用场景的泄漏验证

你提到的托管调用代码:

MYLib.IFlow mySvc = (MyLib.IFlow)MyServices.MySvcCOM;
mySvc.GetTimeoutObject(out _hardwareDoneRecieved);

这里的风险点有两个:

  • 未初始化_hardwareDoneRecieved:如果变量之前持有旧的COM对象引用,调用GetTimeoutObject后旧对象的引用会被覆盖,直接丢失,导致旧对象无法释放。
  • 托管COM互操作的引用计数管理:.NET的RCW(Runtime Callable Wrapper)会自动管理COM对象的引用,但如果变量_hardwareDoneRecieved长期存活(比如作为类成员变量),RCW不会立即释放对象;如果后续没有正确置空或调用Marshal.ReleaseComObject,就会导致对象长时间无法回收,表现为内存泄漏。

这个场景大概率是泄漏的主要来源,建议:

  • 调用前先将_hardwareDoneRecieved置为null,避免覆盖旧引用
  • 使用完后主动调用Marshal.ReleaseComObject(_hardwareDoneRecieved),并将变量置空

非托管代码调用场景的泄漏排查

非托管调用代码:

STDMETHODIMP CSourceBase::get_CoreTimeoutObject( /* [retval][out] */ IUnknown **ppTimeoutObj)
{    
    return GetLocalSvc()->GetTimeoutObject(ppTimeoutObj);
}

这个方法本身逻辑没问题,但需要检查该方法的调用方是否正确处理了返回的IUnknown指针:

  1. 调用方是否在使用完对象后调用了Release()?
  2. 调用前是否确保*ppTimeoutObj为null?如果之前有旧值,会被覆盖导致泄漏。
  3. 该方法作为属性getter(get_CoreTimeoutObject),调用方是否误以为是返回共享实例,而没有释放?

可以通过以下方式验证:

  • 在get_CoreTimeoutObject中添加日志,统计调用次数
  • 配合UMDH的第二次快照,对比该调用路径的分配增量
  • 检查调用方代码,确认是否存在未释放IUnknown指针的情况

修复建议

  1. 先修正GetTimeoutObject的引用计数问题:去掉手动的AddRef(),因为CComObject::CreateInstance返回的对象已经有1次引用,赋值给out参数时不需要额外加引用(COM标准中,out参数的对象引用计数应该由被调用方设置为1,调用方负责释放):
STDMETHODIMP CMyClass::GetTimeoutObject(/* [out] */ IMyInterface **ppTimeoutObj)
{
    ATLASSERT(ppTimeoutObj && !(*ppTimeoutObj));

    CComObject<CCoreTimeoutObject> *pTimeoutObj = NULL;
    HRESULT hr = CComObject<CCoreTimeoutObject>::CreateInstance(&pTimeoutObj);
    if (SUCCEEDED(hr))
    {
        *ppTimeoutObj = static_cast<IMyInterface *>(pTimeoutObj);
        // 移除这里的AddRef()
    }

    return hr;
}
  1. 对托管调用场景,确保调用前清空变量,使用后主动释放
  2. 排查非托管调用方的代码,确认所有获取到的对象都被正确Release()

内容的提问来源于stack exchange,提问作者StackUser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:03:20