Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi temporäre Interfaces sofort freigeben? (https://www.delphipraxis.net/145559-temporaere-interfaces-sofort-freigeben.html)

himitsu 4. Jan 2010 12:48


temporäre Interfaces sofort freigeben?
 
Warum gibt Delphi temporäre Interfaces nicht sofort wieder frei, wenn sie nicht mehr benötig werden?

Bei Strings und dynamischen Arrays versteh ich es ja, da so z.B. der Speicher nochmal verwendet werden kann.

Aber Interfaces verwalten ihren Speicher intern und können ihn garnicht weitergeben,
das heißt, selbst in einer Schleife und in der selben Temp-Variable würden die Interfaces immer wieder "freigegeben" und neu erstellt werden, selbst wenn sie gleich/identisch wären.

aus
Delphi-Quellcode:
for i := 1 to 1000 do
  IntToStr(i);
macht Delphi ja dieses.
Delphi-Quellcode:
var temp: String;

for i := 1 to 1000 do
  temp := IntToStr(i);
Ebenso ist es auch bei Interfaces.

sobald eine Funktion ein Interface als Result zurückliefert oder bei verschachtelten Aufrufen, wird ebenfalls eine temporäre Variable dafür angelegt.

aus [1]
Delphi-Quellcode:
XML.RootDocument.ChildNodes['xyz'].Attributes
wird in etwa dieses [2]
Delphi-Quellcode:
temp1 := XML.RootDocument;
temp2 := temp1.ChildNodes;
temp3 := temp2.Nodes['xyz'];
temp3.Attributes
Das Problem da ist eine beschissene Speicher-/Referenzverwaltung ... vorallem bei verschachtelten Interfaces.
Dadurch wird eventuell nicht alles dort/dann freigegeben, wo man es "vermuten" würde. :wall:


So wurde bei mir z.B. das Document von MSXML dort freigegeben, wo ich es wollte, sondern erst bei Progammende (Consolenanwendung ohne Proceduren und Co., da eh alles Seriell abgearbeitet werden sollte).
Nach einem Hinweis auf dieses Verhalten wurde erstmal jeder größere Schritt in eine Prozedur ausgelagert, wo erstmal dieses Problem verteilt und jeweils bei Prozedurende passierte.

Über 'ne Laufzeitmessung der Prozedurenden, ein massives zerlegen des Codes, eigene temporäre Variablen mit manuell aus-NIL-setzen und vielen Versuchen konnte ich dieses Problem zwar bei mir beheben ... war wichtig 'ne Zeit-/Speichermessung in den einzelnen Programmschritten.

Aber eine gute Lösung ist das nicht wirklich, da der Code so unübersichtlicher wird, wenn man jetzt selber statt [1] nur noch die [2] selber macht.

Alaitoc 4. Jan 2010 13:09

Re: temporäre Interfaces sofort freigeben?
 
Naja so wie ich das sehe werden Interfaces immer dann freigegeben wenn sie nicht mehr verwendet werden.
Wenn man z.b. für IXMLDomDocument eine Membervariable anlegt dann wird sie halt erst freigegeben, wenn
das Objekt an dem sie hängt zerstört wird oder sie explizit auf nil gesetzt wird.

Also in dem Fall:
Delphi-Quellcode:
type
  TBeispiel = class ( TObject )
    private
      m_IXMLDOMDOCUMENT: IXMLDOMDOCUMENT2;
    protected
      { Protected-Deklarationen } 
    public
      { Public-Deklarationen }
  end;
Und wenn sie innerhalb von Methoden verwendet werden, dann werden sie halt am Ende dieser freigegeben ( falls sie nicht irgendwie gesichert wurden z.b. ). Außer man sagt halt wieder explizit das man sie nicht mehr braucht per NIL.

Also in dem Fall:
Delphi-Quellcode:
procedure Beispiel;
var
  IXMLDOMDOCUMENT: IXMLDOMDOCUMENT2;
begin
  IXMLDOMDOCUMENT := coDOMDocument60.Create;
  IXMLDOMDOCUMENT.TUWAS;
end;
Ich halte das soweit für völlig sinnvoll, da Delphi sie halt erst freigibt wenn keine Referenz mehr vorhanden ist.

himitsu 4. Jan 2010 13:18

Re: temporäre Interfaces sofort freigeben?
 
Zitat:

Zitat von Alaitoc
Naja so wie ich das sehe werden Interfaces immer dann freigegeben wenn sie nicht mehr verwendet werden.

Das ist leider ein kleiner aber feiner Irrtum.

Interfaces werden freigegeben, wenn es keine Variablen mehr gibt, wo eine Referenz von ihnen drinsteckt.
(Manipulationen der Referenzzählung mal ausgeschlossen)

[edit]
Beispiel kommt gleich


[edit]
das macht man, bzw. will man machen:
Delphi-Quellcode:
procedure Beispiel;
var
  xml: IXMLDocument;
begin
  xml := coDOMDocument60.Create;
  xml.RootDocument.TUWAS;
  xml := nil; // jetzt würde man denken hier würde alles freigegeben
end; // das passiert aber erst hier
das passiert aber:
Delphi-Quellcode:
procedure Beispiel;
var
  xml: IXMLDocument;
  {$delphiinten} tempnode: IXMLNode; {$ende}
begin
  xml := coDOMDocument60.Create;
  tempnode := xml.RootDocument;
  tempnode.TUWAS;
  xml := nil; // hier passiert nix, da tempnode noch existiert
               // und intern quasi noch eine Referenz auf das Dokument besitzt
end; // hier wird tempnode und erst jetzt erst auch das Dokument freigegeben

Alaitoc 4. Jan 2010 13:19

Re: temporäre Interfaces sofort freigeben?
 
Argh ja :wall: , das versuchte ich damit auszudrücken ^_°

Der Rest stimmt dann aber soweit denk ich x)

MfG Alaitoc

himitsu 4. Jan 2010 13:26

Re: temporäre Interfaces sofort freigeben?
 
Schon OK, im Prinzip stimmt das ja soweit auch.

Nur gibt es da eben dieses klitzekleine Problem ... siehe mein Beispiel in Post #3

sirius 4. Jan 2010 13:53

Re: temporäre Interfaces sofort freigeben?
 
Aber innerhalb einer For-Schleife werden auch die Temps gelöscht
Delphi-Quellcode:
var IntfB:IIntfB;
    i:Integer;
begin

  for i:=1 to 3 do
  begin
    IntfB:=TIntfB.create;
    memo1.lines.add(inttostr(IntfB.IntfA.getNum));
    //IntfB:=nil; mit oder ohne
  end;

Alaitoc 4. Jan 2010 13:56

Re: temporäre Interfaces sofort freigeben?
 
Also wenn ich :

Delphi-Quellcode:
   
if ( GlobalReferenceCount = 0 ) and Assigned( GlobalXMLInterface ) then
    begin
      m_IXMLDocParser := nil; // Hier wird freigegeben
      GlobalXMLInterface := nil; // Hier kann ich nicht mehr auf die geladenen XML-Daten zugreifen
      inherited Destroy;
    end
Oder wie genau überprüfst du das? Hab mir da bisher noch nie so große Gedanken drüber gemacht :gruebel:

Und naja das zweite Beispiel ist mies, da müsste man sich was überlegen...vll irgendwie die Interfaces trennen oder so...

Delphi-Quellcode:
procedure Beispiel;
var
  xml: IXMLDocument;
  {$delphiinten} tempnode: IXMLNode; {$ende}
begin
  xml := coDOMDocument60.Create;
    TuWas(xml.RootDocument);
  xml := nil;          
end;
Da dein Code aber sicherlich um einiges komplexer ist, ist es eine Herausforderung...

himitsu 4. Jan 2010 14:01

Re: temporäre Interfaces sofort freigeben?
 
Zitat:

Zitat von sirius
Aber innerhalb einer For-Schleife werden auch die Temps gelöscht

na das ist ja wohl klar, da dort natürlich immer wieder die selbe Temp-Variable verwendet wird
Delphi-Quellcode:
begin
  xml := coDOMDocument60.Create;
  for i:=1 to 3 do
  begin
    XML.RootDocument.ChildNodes.Nodes['xyz'].Attributes
  end;
  xml := nil;
end;

begin
  xml := coDOMDocument60.Create;
  for i:=1 to 3 do
  begin
    temp1 := XML.RootDocument;
    temp2 := temp1.ChildNodes;
    temp3 := temp2.Nodes['xyz'];
    temp3.Attributes
  end;
  xml := nil;
  // aber das letzte Interface bleibt dennoch bis zum Schluß in diese Variable drinnen.
end;

sirius 4. Jan 2010 14:09

Re: temporäre Interfaces sofort freigeben?
 
Aus irgendwelchen Gründen (vielleicht um es einfach und/oder schnell zu machen) werden lokale (sichtbar oder nicht sichtbar=temporär) Variablen immer am Ende der Funktion gelöscht. Erscheint auch auf den ersten Blick recht logisch.

Und zu deinem Problem:
Tja, himi. Da würde ich sagen: Pech gehabt :stupid:

Im Normalfall ist das Programm derart modularisiert, dass dieses Problem gar nicht erst sichtbar wird. Nur in deinem "Ausnahmefall" hast du ein Problem damit. Was hindert dich daran, dann eben temporäre Variablen sichtbar zu machen (wie du es schon schriebst). Für deinen fall musst du es eben so machen. Tausenden anderen wird dieses durch Delphi erspart.

himitsu 4. Jan 2010 14:19

Re: temporäre Interfaces sofort freigeben?
 
@sirius: ein abschaltbares (gibt ja eh noch nicht genügend geheime Compilerschalter, also kommt es auf einen mehr nicht an) tempvar:=nil; nach gebrauch dieser Interface-Variablen würde vom "Tempo" her nicht viel nehmen und die paar Bytes mehr fallen in den größeren EXEen der neueren Compiler auch nimmer auf,
aber es würde die Speicherverwaltung und Referenzzählung IMHO verbessern.

Die Häufigkeit der Speicherverwaltung und damit das Tempo mag es vielleicht bei Strings und Co. verbessern,
aber bei Interfaces macht dieses eben keinen Unterschied, ob gleich oder erst später.


@Alaitoc:

Aufgefallen ist es praktisch durch dieses
http://www.delphipraxis.net/internal...t.php?t=170881

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

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

local free:[color=#ff0000][b]456[/b][/color]
           [color=#0000ff]^^^ Zeit2[/color]
bei diesem Code kommen die oben genannten Werte raus
- XML:=nil macht nix
- bei Prozedurende wird freigegeben
Delphi-Quellcode:
procedure Test;
  var XML: IXMLDocument;
    Node: IXMLNode;

  begin
    XML := coDOMDocument60.Create;
    XML.Version   := '1.0';
    XML.StandAlone := 'yes';
    XML.Encoding  := 'UTF-8';
    XML.AddChild('xml');
    for i := 0 to 9999 do
      XML.DocumentElement.AddChild(IntToStr(i));
    Start(Zeit1);
    XML := nil;
    Ende(Zeit1);
    Start(Zeit2);
  end;


Test;
Ende(Zeit2);
hier läuft es nun richtig
- bei XML:=nil wird freigegeben
- bei Prozedurende passiert (fast) nix mehr
Delphi-Quellcode:
procedure Test;
  var XML: IXMLDocument;
    Node: IXMLNode;

  begin
    XML := coDOMDocument60.Create;
    XML.Version   := '1.0';
    XML.StandAlone := 'yes';
    XML.Encoding  := 'UTF-8';
    Node2 := XML.AddChild('xml');
    Node2 := nil;
    Node := XML.DocumentElement;
    for i := 0 to 9999 do
      Node2 := Node.AddChild(IntToStr(i));
    Node2 := nil; // oder gleich mit in die Schleife ... ist ja soweit egal
    Node := nil;
    Start(Zeit1);
    XML := nil;
    Ende(Zeit1);
    Start(Zeit2);
  end;


Test;
Ende(Zeit2);

Alaitoc 4. Jan 2010 14:34

Re: temporäre Interfaces sofort freigeben?
 
Ah gut, ja das ist dann wirklich Pech...hm naja ich würds so machen denk ich:

Delphi-Quellcode:
procedure Test;
  var XML: IXMLDocument;
    Node: IXMLNode;

  begin
    XML := coDOMDocument60.Create;
    try
      XML.Version   := '1.0';
      XML.StandAlone := 'yes';
      XML.Encoding  := 'UTF-8';
      Node2 := XML.AddChild('xml');
      Node := XML.DocumentElement;
      for i := 0 to 9999 do
      begin
        Node2 := Node.AddChild(IntToStr(i));
      end;
    finally
      Node2 := nil;
      Node := nil;
      XML := nil;
    end;
  end;
Fänd ich zumindest übersichtlicher und da ich eh immer fast alles selbst freigebe ( auch Interfaces ) ^^ ....
Sollte doch effektiv auf das gleiche hinauslaufen oder ? :gruebel:
Ich muss mich umbedingt mehr mit sowas beschäftigen...

Edit: Ach und ganz wichtig von wegen msXML.DocumentElement.AddChild ! Law of Demeter Da gehe ich zumindest immer nach *g*, außer es geht halt wirklich nicht anders...

himitsu 4. Jan 2010 14:57

Re: temporäre Interfaces sofort freigeben?
 
Zitat:

Zitat von Alaitoc
Ah gut, ja das ist dann wirklich Pech...hm naja ich würds so machen denk ich:

nur wenn es drauf ankommt, dann denkt keiner an Node2 und es kommt nur noch das bei raus
Delphi-Quellcode:
procedure Test;
  var XML: IXMLDocument;
    Node: IXMLNode;

  begin
    XML := coDOMDocument60.Create;
    try
      XML.Version   := '1.0';
      XML.StandAlone := 'yes';
      XML.Encoding  := 'UTF-8';
      XML.AddChild('xml');
      Node := XML.DocumentElement;
      for i := 0 to 9999 do
        Node.AddChild(IntToStr(i));
    finally
      Node := nil;
      XML := nil;
    end;
  end;
theoretisch würde es auch so laufen
Delphi-Quellcode:
procedure Test;
  var XML: IXMLDocument;
    Node: IXMLNode;

  begin
    XML := coDOMDocument60.Create;
    XML.Version   := '1.0';
    XML.StandAlone := 'yes';
    XML.Encoding  := 'UTF-8';
    XML.AddChild('xml');
    Node := XML.DocumentElement;
    for i := 0 to 9999 do
      Node.AddChild(IntToStr(i));
  end;
Hier steckt ja nun alles in einer kleinen Prozedur, aber wenn die Variablen mal länger leben, dann könnte es schon auffallen.

Insgesamt sind diese Prozeduren (samt deiner) etwa gleich schnell und brauchen auch gleich viel Arbeitsspeicher,
aber wo ich dann messen wollte, welcher Schritt wie lange braucht und Dergleichen, da kam halt nur Mist raus.

Zitat:

Zitat von Alaitoc
Edit: Ach und ganz wichtig von wegen msXML.DocumentElement.AddChild ! Law of Demeter Da gehe ich zumindest immer nach *g*, außer es geht halt wirklich nicht anders...

Werd ich mir mal anschauen, auch wenn ich selber MSXML aktuell nur noch für Versuchszwecke nutze. :angel:
[edit]
ach, das gilt allgemein ... na mal sehn, ob ich mir das auch noch Merke :stupid:

Alaitoc 4. Jan 2010 15:14

Re: temporäre Interfaces sofort freigeben?
 
Naja Interfaces benutze ich eh meistens nur in Verbindung mit einer Factory.
Da der Benutzer einer Methode z.b.

Delphi-Quellcode:
  function ErstelleTest: TTest;
ja nicht umbedingt wüßte ob das Objekt freigegeben werden muss. ( Wenn er keine Ahnung von dem Inhalt der Methode hätte )

Bei

Delphi-Quellcode:
  function ErstelleTest: ITEST;
kann man sich sicher sein, dass man sich um Speicherlecks keine Sorgen machen muss.

Und naja das LoD besagt kurz gesagt das man im besten Fall nur einen Punkt benutzen soll.
Also Bsp:

Nicht so:

Delphi-Quellcode:
      XML.DocumentElement.AddChild(IntToStr(i));
Sondern eher so:

Delphi-Quellcode:
  procedure AddChild(Knoten, Inhalt);
  begin
    Knoten.AddChild(InttoStr(i));
  end;

  AddChild(XML.DocumentElement,InttoStr(i));
Auch wenn es auf den ersten Blick mehr Arbeit ist, sind meine Projekte dadurch um einiges übersichtlicher geworden und man bemerkt auch manchmal das der Weg den man gerade einschlägt eher unsauber ist.
So koppelt man die Module nicht so eng miteinander ( zumindest wenn man es richtig macht ).
Dabei helfen einem auch Programmierkonzepte, wie das Factory-Pattern, Command-Pattern, MVC-Pattern, etc...

Btw falls man irgendwo sonst noch bei helfen kann Himitsu sag Bescheid, da ich eh nicht zuhause programmiere... da mir nie etwas sinnvolles einfällt, hätte ich theorethisch oft Zeit zum Helfen...bzw es zu versuchen *gg*


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz