使用MSXML与智能指针的代码存在内存泄漏问题求助
问题描述
使用MSXML智能指针编写的代码存在内存泄漏,循环运行时任务管理器、Process Explorer及GetProcessMemoryInfo获取的Commit Size持续增长。已确认parser指针不是泄漏源,通过手动调用.AddRef()和.Release()发现createProcessingInstruction返回对象的引用计数为2,手动调用.Release()会触发断言,且Debug构建无法进入引用计数逻辑。疑似泄漏点在替换XML处理指令的代码块中。
相关代码:
MSXML2::IXMLDOMDocument3Ptr parser; HRESULT hr(parser.CreateInstance(__uuidof(MSXML2::DOMDocument60) ) ); if(FAILED(hr) ) { throw(XmlException("Failed to create instance of MSXML.")); } parser->preserveWhiteSpace = VARIANT_TRUE; ... // Various methods to populate the XML. Omitted because this was determined to not leak ... // Something within this section is leaking. { const std::wstring wstrXmlDeclaration(L" version=\"1.0\" encoding=\"UTF-16\""); // TODO: If this is expensive, maybe store an instance? IXMLDOMProcessingInstructionPtr pProcessingInstruction( parser->createProcessingInstruction( L"xml", wstrXmlDeclaration.c_str() ) ); if(!pProcessingInstruction) { throw(XmlException("Failed to create instruction for encoding.") ); } DWORD ulRefCount = pProcessingInstruction->AddRef(); ulRefCount = pProcessingInstruction->Release(); // NOTE: ulRefCount is 2 here (??) MSXML2::IXMLDOMNodePtr pFirstChild( parser->GetfirstChild() ); if(!pFirstChild) { throw(XmlException("Failed to get XML Declaration of XML Document.") ); } const std::wstring wstrFirstChildName(pFirstChild->nodeName); assert(wstrFirstChildName == L"xml"); if(wstrFirstChildName != L"xml") { throw(XmlException("Unexpected first element (XML Declaration) in XML.")); } MSXML2::IXMLDOMNodePtr pOldChild( parser->replaceChild( static_cast<MSXML2::IXMLDOMNode*>(pProcessingNode), static_cast<MSXML2::IXMLDOMNode*>(pFirstChild) ) ); if(!pOldChild) { throw(XmlException("Failed to apply specified encoding.") ); } }
泄漏原因分析与修复方案
1. 致命变量名笔误
代码中replaceChild的第一个参数使用了未定义的pProcessingNode,但实际创建的处理指令变量是pProcessingInstruction。这个笔误会导致传入野指针或未初始化指针,MSXML在处理错误指针时,会出现资源未正确清理的情况,直接引发内存泄漏,甚至可能触发断言或崩溃。
修复:将pProcessingNode替换为pProcessingInstruction:
MSXML2::IXMLDOMNodePtr pOldChild( parser->replaceChild( static_cast<MSXML2::IXMLDOMNode*>(pProcessingInstruction), static_cast<MSXML2::IXMLDOMNode*>(pFirstChild) ) );
2. 手动操作引用计数破坏平衡
你手动调用.AddRef()和.Release()的行为会干扰智能指针的自动引用计数管理:
createProcessingInstruction返回的COM对象初始引用计数为1- 智能指针
pProcessingInstruction构造时会自动调用.AddRef(),此时引用计数变为2 - 手动调用
.AddRef()后计数到3,.Release()回到2,这是正常的(智能指针持有1次,对象本身未被DOM树接管时的状态) - 手动修改引用计数会导致后续智能指针析构时的计数不平衡,要么泄漏,要么触发断言
修复:删除手动调用.AddRef()和.Release()的代码,完全依赖智能指针自动管理引用计数。
3. 旧节点的自动释放验证
replaceChild返回的旧节点由pOldChild智能指针持有,代码块结束时智能指针会自动调用.Release()释放旧节点。只要变量名笔误修复,这部分逻辑是正常的,不会导致泄漏。
额外建议
- 使用MSXML的Debug版本(
msxml6d.dll)配合Debug构建,能更精准地捕获COM对象的引用计数问题和内存泄漏,默认Release版本的MSXML会屏蔽部分调试逻辑。 - 避免在智能指针上手动操作引用计数,智能指针的设计初衷就是自动管理COM对象的生命周期。
内容的提问来源于stack exchange,提问作者codesniffer
相关产品推荐
相关产品推荐

