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 fIDocStartas 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)
multithreadingand never the same thread gets thexmldocument interfaces. Now it seems that if I create and get interfaces from the same thread there is no memory 'leak'. May anybody know the reason for this? And that would be a hell to synchronize all running threads to e.g. the main thread when it needs anxmlinterface. Any other suggestion?CoinitializeEx's parameterCOINIT_MULTITHREADEDbut no use.