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指针:
- 调用方是否在使用完对象后调用了
Release()? - 调用前是否确保
*ppTimeoutObj为null?如果之前有旧值,会被覆盖导致泄漏。 - 该方法作为属性getter(
get_CoreTimeoutObject),调用方是否误以为是返回共享实例,而没有释放?
可以通过以下方式验证:
- 在
get_CoreTimeoutObject中添加日志,统计调用次数 - 配合UMDH的第二次快照,对比该调用路径的分配增量
- 检查调用方代码,确认是否存在未释放
IUnknown指针的情况
修复建议
- 先修正
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; }
- 对托管调用场景,确保调用前清空变量,使用后主动释放
- 排查非托管调用方的代码,确认所有获取到的对象都被正确
Release()
内容的提问来源于stack exchange,提问作者StackUser
相关产品推荐
相关产品推荐

