我无法理解Xerces-C++内存管理。
如果我有这个(示例)XML文件" config.xml":
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<port>
<reference>Ref1</reference>
<label>1PPS A</label>
<enabled>true</enabled>
</port>
</settings>
和这段代码:
#include <xercesc/dom/DOM.hpp>
XERCES_CPP_NAMESPACE_USE
DOMElement *nextChildElement(const DOMElement *parent)
{
DOMNode *node = (DOMNode *)parent->getFirstChild();
while (node)
{
if (node->getNodeType() == DOMNode::ELEMENT_NODE)
return (DOMElement *)node;
node = node->getNextSibling();
}
return nullptr;
}
int main(int argc, char **argv)
{
XMLPlatformUtils::Initialize();
XMLCh tempStr[100];
XMLString::transcode("LS", tempStr, 99);
DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
DOMLSParser *parser = ((DOMImplementationLS*)impl)->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
DOMDocument *doc = impl->createDocument(0, 0, 0);
doc = parser->parseURI("config.xml");
DOMElement *el = doc->getDocumentElement(); // <settings>
el = nextChildElement(el); // <port>
el = nextChildElement(el); // <reference>Ref1</reference>
// Heap blows up here
while (1) {
char *cstr = XMLString::transcode(el->getTextContent());
XMLString::release(&cstr); // cstr is "Ref1"
}
// and/or here
while (1) {
XMLCh *xstr = XMLString::replicate(el->getTextContent());
char *cstr = XMLString::transcode(xstr); // cstr is "Ref1"
XMLString::release(&cstr);
XMLString::release(&xstr);
}
}
为什么程序(堆)内存在while (1)
循环中爆炸。两个循环都会导致相同的内存问题:
注意:我使用的是Visual Studio 2017,并且我已在这些配置中对其进行了测试(所有结果都相同):
答案 0 :(得分:2)
问题是函数const XMLCh *getTextConent()
在Document堆上分配内存(使用其MemoryManager),并且没有允许调用者释放内存或将其标记为可循环使用的规定。因此,一旦返回的指针从调用者的堆栈中删除,内存基本上是孤立的,直到整个Document被释放,此时MemoryManager将删除所有堆分配。
解决方案是不使用getTextContent()
,而是使用getNodeValue()
代替,它返回指向数据的指针,而不是从内部堆重新分配它。
除此之外,getTextContent无论如何都不起作用。因为所有人都出局并且实际上没用,这是错误的。如果存在非相邻的Text节点,则无法以这种方式读取DOM或者在各种不同的情况下会得到不准确的数据(如果没有,则无论如何都不需要使用它,因为直接节点值就是你所需要的。)
因此,OP示例代码的工作版本可能如下所示:
#include <xercesc/dom/DOM.hpp>
#include <string>
XERCES_CPP_NAMESPACE_USE
DOMElement *nextChildElement(const DOMElement *parent)
{
DOMNode *node = (DOMNode *)parent->getFirstChild();
while (node)
{
if (node->getNodeType() == DOMNode::ELEMENT_NODE)
return (DOMElement *)node;
node = node->getNextSibling();
}
return nullptr;
}
std::string readTextNode(const DOMElement *el)
{
std::string sstr;
DOMNode *node = el->getFirstChild();
if (node->getNodeType() == DOMNode::TEXT_NODE) {
char *cstr = XMLString::transcode(node->getNodeValue());
sstr = cstr;
XMLString::release(&cstr);
}
return sstr;
}
int main(int argc, char **argv)
{
XMLPlatformUtils::Initialize();
XMLCh tempStr[100];
XMLString::transcode("LS", tempStr, 99);
DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
DOMLSParser *parser = ((DOMImplementationLS*)impl)->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
DOMDocument *doc = impl->createDocument(0, 0, 0);
doc = parser->parseURI("config.xml");
DOMElement *el = doc->getDocumentElement(); // <settings>
el = nextChildElement(el); // <port>
el = nextChildElement(el); // <reference>Ref1</reference>
// No memory leak
std::string nodestr;
while (1) {
nodestr = readTextNode(el); // nodestr is "Ref1"
}
}