使用Delphi7 XMLDoc的内存问题

时间:2013-10-25 11:03:01

标签: xml delphi memory-leaks xml-documentation

我正在使用Delphi 7 XMLDoc单元在XML基础上构建协议定义,即协议在XML文件中定义,一些对象从此XML获取信息,用于分解和构建在TCP上传递的ISO8583消息/ IP。我广泛使用IXMLDocumentIXMLNode接口。一切都很好,在终止点没有内存泄漏(使用FASTMM)。但在运行期间,软件会慢慢耗尽内存。释放对象时以某种方式释放内存 - 从TXMLDocument派生 - 我用于XML访问。我分析并发现,如果我没有使用IXMLDocumentIXMLNode接口,则没有内存问题,但我当然必须这样做。

我有一个来自TXMLTree的对象(TXMLDocmument):

TXMLTree = class(TXMLDocument, IXMLDocument)
...
end;

我有另一个对象(TDPR)并在其构造函数中创建TXMLTree并获取接口(fIDocument)以及文档起点的接口(fIDocStart )以及:

TDPR = class(TObject)
...
  fIDocument:IXMLDocument;
  fIDocStart:IXMLNode;
...
end;

constructor TDPR.Create(aFilename: string);
begin
  inherited; 
...
  fTree := TXMLTree.Create(aFilename);
  fTree.GetInterface(IXMLDocument, fIDocument);

  fIDocStart := fIDocument.DocumentElement;
...
end;

通常我使用fIDocumentfIDocStart作为到达XML文档的起点,编写如下代码:

node := fIDocument.DocumentElement.ChildNodes[aName].ChildNodes.FindNode(aFieldName);

node := fIDocStart.ChildNodes[aName].ChildNodes.FindNode(aFieldName);

只有一条这样的线足以缓慢增加内存使用量,但如果我在for循环中执行此操作,例如千万没有更多的效果,因为 - 我猜 - 只有一个参考被使用并在循环中一直释放。

现在让我们有一个程序Foo,在那里我得到文件的起点:

procedure TDPR.Foo;
var lNode:IXMLNode;
begin
  lNode := IDocument.DocumentElement;
end;

如果我调用此程序会稍微增加内存使用量,但会定期调用它,只是时间耗尽的问题。
但是:如果我使用fIDocStart已经存储了所需的接口 - 所以不需要一次又一次地调用DocumentElement - 没有内存问题。

procedure TDPR.Foo;
var lNode:IXMLNode;
begin
  lNode := fIDocStart;
end;

所以似乎每个获取并返回接口对象的调用(IXMLDocument IXMLNode IXMLNodeList等等)都会分配一些内存,并且在正确释放引用对象时不释放它。即使我们要求相同的节点。当然,我不能存储所有可能的节点只是为了避免耗尽内存。

TDPR的析构函数中,我通过将对应接口设置为TXMLTree来释放nil,并且在终止时没有检测到内存泄漏。参考计数器似乎没问题。

destructor TDPR.Destroy;
begin
...
  fIDocStart := nil;
  fIDocument := nil;

...
  inherited; 
end;

这与大型XML文件无关。问题是XML这样的问题:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"?>
<Base24-POS>
   <Instructions>
      <Parameters Receive="1"/>
   </Instructions>
</Base24-POS>

我知道这是模糊不清的,我只是要求有机会遇到同样的问题并找到解决方案。 (顺便说一句,我记得在WindowsXP中使用完全相同的代码没有任何问题。现在它是Windows7)

1 个答案:

答案 0 :(得分:0)

我发现问题与多线程访问COM对象有关(XMLDocument是COM)。创建TXMLDocument并通过单个线程上的IXMLDocumentIXMLNode等接口对其进行操作时,没有内存“泄漏”。但是使用不同的线程我无法解决问题。我使用CoInitializeEx和参数COINIT_MULTITHREADED。似乎每个线程在获取接口时都会分配一些内存并且不会释放它,并且每个线程都会分配一次 - 至少对于某个接口,所以一个线程不会导致可见的内存泄漏。但动态创建的线程都无法释放内存并最终消耗进程内存。到目前为止,我找不到任何解决方案,所以我考虑使用SetProcessWorkingSet(ProcessHandle, -1, -1)定期清理记忆,并且症状治疗解决了问题,直到达到更好的解决方案。我还提出了一个专门解决这个问题的新问题Memory consumption when using Delphi7 COM interfaces in a multithreaded way