I'm using Delphi 7 XMLDoc unit for building a protocol definition on XML base, that is the protocol is defined in an XML file and some objects are get information from this XML for breaking-down and constructing ISO8583 messages delivered on TCP/IP. I widely make use of IXMLDocument
and IXMLNode
interfaces. Everything is fine, no memory leaks (using FASTMM) at point of termination. But during its running the software slowly consumes up memory. The memory is somehow released when freeing the object - derived from TXMLDocument
- I'm using for XML access. I analysed and found that there is no memory problem if I did not use IXMLDocument
and IXMLNode
interfaces but of course I have to.
I have an object (TXMLTree
) deriving from TXMLDocmument
:
TXMLTree = class(TXMLDocument, IXMLDocument)
...
end;
I have another object (TDPR
) and in its constructor I create TXMLTree
and get the interface (fIDocument
) plus an interface to the document's starting point (fIDocStart
) as well:
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;
Usually I use fIDocument
and fIDocStart
as a starting point to reach the XML doc, writing code like this:
node := fIDocument.DocumentElement.ChildNodes[aName].ChildNodes.FindNode(aFieldName);
or
node := fIDocStart.ChildNodes[aName].ChildNodes.FindNode(aFieldName);
Only one such line is enough to slowly increase memory usage, but if I perform it in a for
loop of e.g. ten millions there is not more effect because - I guess - only one reference is used and released all the way in the loop.
Now let's have a procedure Foo
where I get the document starting point:
procedure TDPR.Foo;
var lNode:IXMLNode;
begin
lNode := IDocument.DocumentElement;
end;
If I call this procedure it increases memory usage a bit, but call it periodically and it is only question of time to run out of memory.
But: if I use fIDocStart
which already stores the needed interface - so no need to call DocumentElement
again and again - there is no memory problem.
procedure TDPR.Foo;
var lNode:IXMLNode;
begin
lNode := fIDocStart;
end;
So it seems that every call that gets and returns an interface object (IXMLDocument
IXMLNode
IXMLNodeList
and so on) allocate some memory and does not frees it while reference objects are properly released. Even if we ask for the same node. Of course I cannot store all possible nodes just to avoid using up memory.
In TDPR
's destructor I free TXMLTree
by setting the correspondent interface to nil
and no memory leaks is detected at termination. Reference counters seems to be ok.
destructor TDPR.Destroy;
begin
...
fIDocStart := nil;
fIDocument := nil;
...
inherited;
end;
And it is nothing to do with large XML
files. The problem is the same whith XML
like this:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"?>
<Base24-POS>
<Instructions>
<Parameters Receive="1"/>
</Instructions>
</Base24-POS>
I know it is obscure and I only ask to get the chance that somebody has already bumped into the same problem and may found a solution. (BTW I remember using quite the same code in WindowsXP with no trouble. Now it is Windows7)