Xerces-C ++内存

时间:2018-04-05 16:29:02

标签: c++ xerces-c

我无法理解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)循环中爆炸。两个循环都会导致相同的内存问题:

xerces memory diagnostics

注意:我使用的是Visual Studio 2017,并且我已在这些配置中对其进行了测试(所有结果都相同):

  • xerces-c-3.2.1,static lib,x64
  • xerces-c-3.2.1,dynamic(dll),x64
  • xerces-c-3.1.2,static lib,x64

1 个答案:

答案 0 :(得分:2)

问题是函数const XMLCh *getTextConent()在Document堆上分配内存(使用其MemoryManager),并且没有允许调用者释放内存或将其标记为可循环使用的规定。因此,一旦返回的指针从调用者的堆栈中删除,内存基本上是孤立的,直到整个Document被释放,此时MemoryManager将删除所有堆分配。

解决方案是不使用getTextContent(),而是使用getNodeValue()代替,它返回指向数据的指针,而不是从内部堆重新分配它。

this (non)-bug report

  

除此之外,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"
    }
}