我正在使用Delphi 7 XMLDoc单元在XML基础上构建协议定义,即协议在XML文件中定义,一些对象从此XML获取信息,用于分解和构建在TCP上传递的ISO8583消息/ IP。我广泛使用IXMLDocument
和IXMLNode
接口。一切都很好,在终止点没有内存泄漏(使用FASTMM)。但在运行期间,软件会慢慢耗尽内存。释放对象时以某种方式释放内存 - 从TXMLDocument
派生 - 我用于XML访问。我分析并发现,如果我没有使用IXMLDocument
和IXMLNode
接口,则没有内存问题,但我当然必须这样做。
我有一个来自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;
通常我使用fIDocument
和fIDocStart
作为到达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)
答案 0 :(得分:0)
我发现问题与多线程访问COM对象有关(XMLDocument是COM)。创建TXMLDocument
并通过单个线程上的IXMLDocument
和IXMLNode
等接口对其进行操作时,没有内存“泄漏”。但是使用不同的线程我无法解决问题。我使用CoInitializeEx和参数COINIT_MULTITHREADED
。似乎每个线程在获取接口时都会分配一些内存并且不会释放它,并且每个线程都会分配一次 - 至少对于某个接口,所以一个线程不会导致可见的内存泄漏。但动态创建的线程都无法释放内存并最终消耗进程内存。到目前为止,我找不到任何解决方案,所以我考虑使用SetProcessWorkingSet(ProcessHandle, -1, -1)
定期清理记忆,并且症状治疗解决了问题,直到达到更好的解决方案。我还提出了一个专门解决这个问题的新问题Memory consumption when using Delphi7 COM interfaces in a multithreaded way。