Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#1

MSXML und seine Speicherverwaltung?

  Alt 29. Dez 2009, 12:52
Moin,

in meinem himXML hab ich unter anderem diesen TestCode (in CheckLibs.dpr)
Delphi-Quellcode:
Var msXML: XMLDoc.TXMLDocument;
   msNode: XMLIntf.IXMLNode;

msXML := XMLDoc.TXMLDocument.Create(nil);
Try
  msXML.DOMVendor := XMLDOM_Vendors[i4].Vendor;
  msXML.Active := True;
  msXML.Version := '1.0';
  msXML.StandAlone := 'yes';
  msXML.Encoding := 'UTF-8';
  msXML.Options := [doNodeAutoIndent];
  msXML.AddChild('xml');
  For i := 0 to 9999 do msXML.DocumentElement.AddChild(Node_IntToStr[i]);
nu hat jemand (samso) rausgefunden, daß dort die Interfaceverwaltung beim SimpleXML schlecht ist ... wodurch einige Objekte (vorallem SimpleXML und auch MSXML) nicht korrekt freigegeben wurden.
Angeblich (laut Messung) ging das superflott innerhalb von etwa 0 ms , aber nun stellte sich raus, daß die Objekte garnicht freigegeben wurden.

Zitat von samso:
Hallo,

ich habe CheckLibs ein wenig überarbeitet. Dabei ging es mir überwiegend um das Testen von SimpleXML. Bei SimpleXML ist es ausgesprochen suboptimal den Knoten sozusagen "offline" zu erzeugen und ihn dann im nächsten Schritt in den XML-Baum einzuklinken. Besser ist die Verwendung von "AppendElement".

Dann bereitet leider das Freigeben der Interfaces ausgesprochenen Ärger. Delphi mag temporäre Interfaces freiwillig erst beim Verlassen der Procedure freigeben. D.h. temporäre Interfaces müssen manuell freigeben werden.
Also so:
Delphi-Quellcode:
siRoot := siXML.DocumentElement;
For i := 0 to 9999 do Begin
  siNode := siRoot.AppendElement(Node_IntToStr[i]);
  siNode := nil;
End;
siRoot := nil;
Statt so:
Delphi-Quellcode:
For i := 0 to 9999 do Begin
  siXML.DocumentElement.AppendElement(Node_IntToStr[i]);
End;
Trotzdem mag Delphi global deklarierte Interfaces irgendwie gar nicht freigeben. Deshalb habe ich alle Tests in entsprechende Proceduren verpackt.
OK, das AppendElement statt meinem CreateXmlElement+AppendChild machte jetzt im Test erstmal keinen großen Unterschied, da AppendElement ebenfalls intern sowas wie CreateXmlElement+InsertChild ausführt.

bei mir sah/sieht es im Testprojekt so aus
Delphi-Quellcode:
For i := 0 to 9999 do Begin
  siNode := SimpleXML.CreateXmlElement(Node_IntToStr[i]);
  siXML.DocumentElement.AppendChild(siNode);
End;
(aber mit dem Hauptproblem der Interfaceverwaltung hat es nichts zu tun)

Zitat von samso:
Jau, schon klar!
Mein Problem war Test 6. Dort wird der Speicher zwischen erzeugen der 3Mio Knoten und dem Lesen der Datei leider nicht freigegeben. Dies führt dann dazu das der Test wegen Speichermangel abgebrochen wird. Deshalb habe ich beide Tests getrennt. Das Verhalten kann man auch bei den anderen Tests erkennen, weil hier das Freigeben üblicherweise in 0ms erfolgt - ergo - es wird nichts freigeben.
D.h. bei globaler Variable:
Zitat:
***** Test 4 ************************************************** ****************

fill TXMLFile with 100.000 nodes, save into and load this from a file
create:0 fill:191 save:109 free:36
create:0 load:409 free:41

fill SimpleXML with 100.000 nodes, save into and load this from a file
create:0 fill:99 save:123 free:0
create:0 load:312 free:0
Bei lokaler Variable
Zitat:
***** Test 4 ************************************************** ****************

fill TXMLFile with 100.000 nodes, save into and load this from a file
create:0 fill:193 save:107 free:37
create:0 load:414 free:36

fill SimpleXML with 100.000 nodes, save into and load this from a file
create:0 fill:109 save:123 free:45
create:0 load:311 free:41

Delphi-Quellcode:
Var msXML: XMLDoc.TXMLDocument;
   msNode: XMLIntf.IXMLNode;

msXML := XMLDoc.TXMLDocument.Create(nil);
Try
  msXML.DOMVendor := XMLDOM_Vendors[i4].Vendor;
  msXML.Active := True;
  msXML.Version := '1.0';
  msXML.StandAlone := 'yes';
  msXML.Encoding := 'UTF-8';
  msXML.Options := [doNodeAutoIndent];
  msNode := msXML.AddChild('xml');
  msNode := nil;
  For i := 0 to 9999 do msNode := msXML.DocumentElement.AddChild(Node_IntToStr[i]);
  msNode := nil;
Nachdem ich nun selber nochmal alles umgestellt und auch mal die Zeit für das Ende ( end. ) der einzelnen Prozeduren mitgemessen hatte, stellte sich der Selbe Effekt auch beim MSXML raus, aber leider komme ich hier nicht weiter

Im Obrigen Code hab ich nurn also mal alle "temporäten" Variablen entfernt, durch eigene ersetzt und diese manuell freigegeben:
Delphi-Quellcode:
Var msXML: XMLDoc.TXMLDocument;
   msNode: XMLIntf.IXMLNode;

msXML := XMLDoc.TXMLDocument.Create(nil);
Try
  msXML.DOMVendor := XMLDOM_Vendors[i4].Vendor;
  msXML.Active := True;
  msXML.Version := '1.0';
  msXML.StandAlone := 'yes';
  msXML.Encoding := 'UTF-8';
  msXML.Options := [doNodeAutoIndent];
  msNode := msXML.AddChild('xml');
  msNode := nil;
  For i := 0 to 9999 do
    msNode := msXML.DocumentElement.AddChild(Node_IntToStr[i]);
  msNode := nil;
so weit so gut ... nur gibt es jetzt ein "klitzekleines" Problem, denn es läuft nichts mehr

msXML läßt sich so nicht als IXMLDokument (Interface) nutzen, da ich noch keine andere Möglichkeit fand dort den DOMVendor anders zu nutzen.

Probem ist jetzt, sobald der Rootnode, welcher von msXML.AddChild('xml') zurückgegeben wird, freigegeben wurde, wird auch das ganze Dokument freigegeben und es kommmt beim Erstellen des ersten Nodes innerhalb der Schleife zu einer Exception.
Zitat:
Im Projekt CheckLibs.exe ist eine Exception der Klasse EXMLDocError mit der Meldung 'Kein aktives Dokument' aufgetreten.
OK, nach etwas Rumprobieren hab ich es nun so
Delphi-Quellcode:
Var msXMLc: XMLDoc.TXMLDocument;
    msXML: XMLIntf.IXMLDocument;
    msNode: XMLIntf.IXMLNode;

msXMLc := XMLDoc.TXMLDocument.Create(nil);
Try
  Try
    msXMLc.DOMVendor := XMLDOM_Vendors[i4].Vendor;
    msXMLc.Active := True;
    msXMLc.Version := '1.0';
    msXMLc.StandAlone := 'yes';
    msXMLc.Encoding := 'UTF-8';
    msXMLc.Options := [doNodeAutoIndent];
  Finally
    msXML := msXMLc;
  End;
  msNode := msXML.AddChild('xml');
  msNode := nil;
  For i := 0 to 9999 do msNode := msXML.DocumentElement.AddChild(Node_IntToStr[i]);
  msNode := nil;
  msXML.SaveToFile(ChangeFileExt(ParamStr(0), '.Test1_MSXML.xml'));
  msXML := nil; // << free
Except
  On E: Exception do Begin
    WriteLn(E.Classname, ': ', E.Message);
    msXML := nil;
  End;
End;
aber auch hier wird das Dokument noch nicht dort Freigegeben wo es sollte (<< free) .

so geht es ebenfalls nicht
Delphi-Quellcode:
msNode := msXML.AddChild('xml');
msNode := nil;
For i := 0 to 9999 do Begin
  msNode := msXML.DocumentElement.AddChild(Node_IntToStr[i]);
  msNode := nil;
End;
msXML.SaveToFile(ChangeFileExt(ParamStr(0), '.Test1_MSXML.xml'));


Code:
***** Test 1 ******************************************************************

fill TXMLFile with 10.000 nodes and save this into a file
create:8  fill:7  save:4  free:3

fill MS-XML-DOM with 10.000 nodes and save this into a file
create:21  fill:13006  save:110  [color=#ff0000]free:0[/color]

[color=#ff0000]local free:456[/color]
Code:
***** Test 1 ******************************************************************

fill TXMLFile with 10.000 nodes and save this into a file
create:0  fill:7  save:6  free:3

[color=#ff0000]local free:0[/color]
ohne MSXML wird die Prozedur schnell beendet, also wurde irgendwas vom MSXML nicht freigegeben
und ich finde einfach nichts mehr ... keine weitere Prozedur hat noch einen Rückgabewert mit 'nem Interface, welche von Delphi als "temporäre" Variable gepsichert und somit erst bei Prozedurende, wo dann auch alle temporären Variablen freigeben werden, welche wie lokale Variablen behandelt werden ... zumindestens finde ich Keine.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat