我无法理解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>
Run Code Online (Sandbox Code Playgroud)
而这段代码:
#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);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么程序(堆)内存在while (1)循环中爆炸.两个循环都会导致相同的内存问题:
注意:我正在使用Visual Studio 2017,并且我已经在这些配置中对其进行了测试(所有结果都相同):
问题是函数const XMLCh *getTextConent()在 Document 堆上分配内存(使用其 MemoryManager),并且没有规定允许调用者释放内存或将其标记为回收。因此,一旦返回的指针从调用者的堆栈中删除,内存基本上就成为孤立的,直到整个 Document 被释放,此时 MemoryManager 会删除所有堆分配。
解决方案是不使用getTextContent(),而是使用getNodeValue(),它返回指向数据的指针,而不是从内部堆中重新分配它。
根据此(非)错误报告
除此之外, getTextContent 无论如何都不起作用。这是有缺陷的,因为所有的东西都出去了并且实际上毫无用处。您无法以这种方式读取 DOM,否则如果存在不相邻的 Text 节点,您将在各种不同的情况下得到不准确的数据(如果不存在,则无论如何都不需要使用它,因为直接节点值将是您所需要的)。
因此,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"
}
}
Run Code Online (Sandbox Code Playgroud)