Announcement

Collapse
No announcement yet.

Memory Leak?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Memory Leak?

    hallo COM - Gemeinde,

    wenn ich mit einen Variant verwende um auf die Methoden eines
    COM-Objekts zuzugreifen, und dem Variant am Ende

    z.B.: <b>Unassigned</b> oder NULL zuweise, steigt der
    MemUsage meines Prozesses im Task-Manager um <b>4k</b> bei jedem klick
    auf den "Button1".

    Für mich sieht das so aus, als würde ich etwas nicht freigeben.<br>
    Ich habe im meiner Verzweiflung versucht, _Release aufzurufen, um den<br> Referenzzähler herunter zu setzen, war damit aber auch nicht erfolgreich.<br>
    <p>
    Was ist der Grund dafür?<br>
    Mein COM Objekt (nicht wie im folgenden Bespiel MSWord)
    wird unter umständen einige Male pro Sekunde aufgerufen,
    was mit der Zeit zu einem gewissen Problem führen wird...

    Ich verwende eigentlich ein anderes COM Objekt, aber mit dem häufig
    verwendeten Word-Beispiel komme ich zu dem selben 4k-Anstieg
    pro Aufruf:

    <PRE>
    procedure TForm1.Button1Click(Sender: TObject);
    var
    MSWord: Variant;
    begin
    MSWord := CreateOleObject('Word.Application');
    MSWord.Quit;
    MSWord := Unassigned;
    end;
    </PRE>

  • #2
    Ich habe unter dynamisch geladenen DLL's mal einen Beitrag zu einem ählichen Problem gefunden, vielleicht hilft Dir das ja weiter:
    http://www.entwickler-forum.de/webx?128@@.ee6c72e<p>
    Schöne Grüße, Mario Noac
    Schöne Grüße, Mario

    Comment


    • #3
      <pre>
      hallo Mario,

      herzlichen Dank für den Link

      ich war jedoch leider mit dem
      <b>procedure</b> DeleteObjectInstance; - Patch leider nicht erfolgreich

      </pre&gt

      Comment


      • #4
        Moin Dr Watson,<br>
        <br>
        wenn ich nicht sehr irre bewirkt die Zuweisung von Unassigned nichts weiter, als würde ich einer Variablen die auf ein Objekt zeigt nil zuweisen. Die Adresse ist nicht mehr bekannt, aber der Speicher bleibt belegt.<br>
        Hast Du es schon mal mit einem schlichten MSWord.Free vor der Zuweisung von Unassigned versucht?<br>
        (Vielleicht ginge ersatzweise ja auch FreeAndNil(MSWord))<br>
        <br>
        Ciao<br>
        Chris<br>
        <br>
        PS: Vielleicht liege ich auch völlig falsch. COM ist nicht so meine Welt

        Comment


        • #5
          hallo Chris,

          danke für die Tips, <br>
          <br>
          was das Zuweisen v. Unassigned bzw. NULL betrifft, bin ich einer Meinung mit Dir, daß das meinen Speicher nicht aufräumen wird. <br>
          <br>
          ich war jedoch leider auch mit Deinen Vorschlägen erfolglos <br>
          <p>
          MSWord hat leider keine Methode "Free", die ich aufrufen könnte.
          <p>
          FreeAndNil(MSWord) verursacht eine Schutzverletzung, der Grund
          dafür scheint dieser zu sein:<br>
          <p>
          Aus der Online-Hilfe von Delphi:<br>
          Do not pass a value for Obj if it is not an instance of TObject or a TObject descendant.<br>
          <br>
          Trotzdem 1k Dank fürs Helfen <br>
          <br>
          lg Alex<br>
          <br>
          P.S.: Ich habe MSWord nur als Beispiel angegeben, da daß für die meisten nachvollziehbar sein wird. Die Sache mit den 4k passiert mir auch, wenn ich von anderen COM-Klassen mit einem Variant eine Instanz erzeuge

          Comment


          • #6
            Und was ist, wenn Du expliziet Release aufrufst?

            - nic

            Comment


            • #7
              Hallo,

              der Zugriff über die späte Bindung (OleVariant als Variable für den Interface-Zeiger) hat einige Nebenwirkungen. Was passiert, wenn die Typbibliothek importiert und eingebunden wird, so dass der Zugriff über die frühe Bindung (Dual Interface) möglich wird? <br>Handelt es sich um einen In-Process Server (DLL) oder um einen Local/Remote Server (EXE)? Im Fall der DLL gibt es das Problem mit dem Speicher-Leck bei jedem Ladevorgang, so dass die DLL besser nur einmal in den Prozess geladen werden sollte. Wird der Prozess (Client-EXE) später geschlossen, räumt Win32 hinterher diese 4 kByte-Leiche automatisch ab.

              >....einige Male pro Sekunde aufgerufen

              In diesem Fall würde ich <b>niemals</b> eine lokale Variable für den Interface-Zeiger verwenden, denn eine lokale Variable verliert bei jedem Aufruf ihren Gültigkeitsbereich. Besser ist ein privates Objektfeld, so dass die Lebensdauer des COM-Objekts mit der Lebensdauer des Client-Prozesses übereinstimmt. Der Zugriff ist dann um Größenordnungen schneller und auch das Problem des Speicher-Lecks ist dann irrelevant

              Comment


              • #8
                hallo,

                danke für den Rat,<br>
                <br>
                Wenn ich das Type-Library (des COM-Objekts von meiner Kollegin)
                importiere, und mit dem folgenden Codeausschnitt teste, komme ich
                zu einem ähnlichen Ergebnis wie zuvor. In diesem Fall wächst mein Speicherverbrauch in 28k Schritten:

                <PRE>
                interface
                uses
                ..., DBCLIENTLib_TLB;
                .
                .
                implementation
                procedure TForm1.Button1Click(Sender: TObject);
                var
                dc: TComstarDBClient;
                begin
                dc := TComstarDBClient.Create(Application);
                dc.Destroy;
                end;
                </PRE>
                <br>
                <br>
                Der "ComstarDBClient" ist ein In-Process Server. <br>
                <br>
                Ich bin dabei einen In-Process Server zu schreiben "seCMUtility", der den "ComstarDBClient" verwendet.<br>
                Einer meiner Kollegen hat meinen In-Process Server ("seCMUtility") auf Memory Leaks untersucht,<br>
                indem er ihn "ein paar mal" erzeugt und zerstört hat.
                Wenn ich den ComstarDBClient in meinem In-Process Server auskommentiere, ist es vorbei mit dem Memory Leak.<br>
                <br>
                So wie es aussieht, muß ich meinen Kollegen, der den In-Process Server ("seCMUtility") verwendet<br>
                dazu überreden, daß er ihn nicht immer "just in time" neu erzeugt...<br>
                <br>
                Ich hab also einen "leaken" In-Process Server gebastelt?!
                Wie bring ich das meinen Kollegen bei, die fast alle mit C++ programmieren?! - Vertuschen kann ich das wohl nicht mehr
                <br>
                <br>
                ...in diesem Fall würde ich niemals eine lokale Variable für den Interface-Zeiger verwenden...<br>
                die Objektvariable f. "ComstarDBClient" wird seit kurzem im Private-Teil einer Klasse definiert, die von meinem In-Process Server instaziert wird. Wenn man das Ganze nur 1x erzeugt und freigiebt,
                ist die Sache ok.<br>
                <br>
                <br>
                Woher kommen eigentlich diese 4k Leichen, gibt es dazu auch einen Patch?<br>
                Der Link von Mario Noack hat ein solches 4k-Problem gelöst, leider nur
                nicht das in meinem Fall.<br>
                <br>
                <br>
                lg Alex<br>
                P.S.: Danke für die Hilf

                Comment


                • #9
                  Hallo,

                  &gt;dc: TComstarDBClient;

                  wenn die <b>Klasse</b> und nicht das Interface als lokale Variable deklariert wird, gibt es <b>keine</b> automatische Freigabe des allozierten Speichers! Delphi räumt nur dann automatisch ab, wenn ein <b>Interface-Zeiger</b> deklariert wird, wobei trotzdem TComstarDBClient.Create unterstützt wird. Der bessere Weg ist allerdings der direkte Aufruf der class methode <i>CoXYT.Create</i> aus der TLB.pas:
                  <pre>
                  var
                  aMWSt : IMWSt;
                  begin
                  aMWSt := CoMWSt.Create;
                  aMWSt.DoWork;
                  end;
                  </pre>
                  Da die lokale Variable als Interface-Zeiger deklariert wird, räumt Delphi automatisch dann ab, wenn diese Variable ihren Gültigkeitsbereich (Methode) verlässt.

                  &gt;Woher kommen eigentlich diese 4k Leichen, gibt es dazu auch einen Patch?

                  Dieses Leiche ist von Anfang an tief in der VCL vergraben. Jedesmal wenn <b>MakeObjectInstance</b> aufgerufen wird, gehen mindestens 4 kByte RAM verloren. Es gab vor Urzeiten mal einen Bugfix, den ich aber nicht ausprobiert habe:
                  <pre>
                  procedure DeleteObjectInstance;
                  Var
                  Block : PInstanceBlock;
                  Begin
                  While InstBlockList <> NIL do
                  Begin
                  Block := InstBlockList^.Next;
                  VirtualFree(InstBlockList, 4096, MEM_DECOMMIT);
                  InstBlockList := Block;
                  end;
                  end;
                  </pre>
                  &#10

                  Comment


                  • #10
                    Hallo,

                    Danke für die Hilfe und für "DeleteObjectInstance", ich werde die Sache bei Gelegenheit ausprobieren.<br>
                    <br>
                    Am Ende noch einmal ein Überblick, wie es jetzt läuft:

                    <pre>

                    ...
                    procedure TseCMUtility.Initialize;
                    begin
                    inherited Initialize;
                    try
                    DataImport := TDataImport.Create;
                    except
                    ...
                    end; //try
                    end;

                    destructor TseCMUtility.Destroy;
                    begin
                    try
                    DataImport.Destroy;
                    except
                    ...
                    end;
                    inherited Destroy;
                    end;
                    .
                    .
                    initialization
                    TTypedComObjectFactory.Create(ComServer, TseCMUtility, Class_seCMUtility_, ciSingleInstance, tmApartment);

                    -------------------------------------------------
                    ...
                    uses DBClientLib_TLB,...
                    ...
                    TDataImport = class
                    public
                    function FileToDb(const XMLCallListString: WideString;
                    out RetVal: Integer): HResult; stdcall;
                    ...
                    private
                    DBClient: TComstarDBClient;
                    ...
                    end;

                    implementation
                    uses

                    Constructor TDataImport.Create;
                    begin
                    try
                    DBClient := TComstarDBClient.Create(AOwner);
                    except
                    ...
                    end;
                    end;

                    Destructor TDataImport.Destroy;
                    begin
                    DBClient.Destroy;
                    ...
                    end;
                    </pre>
                    <br>
                    <br>
                    mfg Alexander Stracke

                    Comment

                    Working...
                    X