Announcement

Collapse
No announcement yet.

Garbage Collector

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

  • Garbage Collector

    Ist es möglich dass der Garbage Collector nicht richtig funktioniert?
    Wenn ich Formulare auf und zu mache, benötigt das Programm immer mehr speicher, gibt aber nichts mehr frei. Erst beim beenden.

    Das selbe Problem tritt auf wenn ich eine ASP.NET-Anwendung unter Delphi 8 erstelle. Der Prozess (aspnet_wp.exe) benötigt immer mehr Speicher, obwohl ich meinen Browser schon längst geschlossen habe. Das wird schon zum Problem wenn viele Leute die ASP.NET-Anwendung starten.

    mfG
    Manuel Egger

  • #2
    Hallo,

    >Ist es möglich dass der Garbage Collector nicht richtig funktioniert?

    Nein, der GC arbeitet genau so wie dokumentiert. Allerdings kümmert sich der GC nur um den managed Code, alle direkten Allozierungen von nativen Win32-Handles etc. müssen wie gehabt in eigener Regie freigegeben werden. Tritt dieses Problem bei einer FCL-Anwendung (Windows Forms) oder bei einer VCL.NET-Anwendung auf?

    >..wenn ich eine ASP.NET-Anwendung unter Delphi 8 erstelle.

    Wird dort BDP.NET verwendet (d.h. werden diese Assemblies als Verweise ins Projekt eingebunden)? Wenn ja, hat das mit dem GC nichts zu tun, sondern mit einem Bug in BDP.NET. Das Speicherleck verschwindet sofort, wenn BDP.NET durch die originalen ADO.NET-Provider ersetzt wird

    Comment


    • #3
      Dieses Problem tritt bei einer VCL.NET-Anwendung auf. Ich verwende nur managed Code!
      Ich erzeuge ein Formular, zeige es an und schreibe danach in das Objekt <b>nil</b> hinein:
      <pre>
      ...
      var
      FormOjbect: TMyForm;
      begin
      FormOjbect:=TMyForm.Create();
      FormOjbect.ShowModal;
      FormOjbect:=nil;
      end;
      </pre>

      Im Form werden Bilder geladen und angezeigt.<br>
      Speicher vorm Öffnen: 33 MB<br>
      Speicher nach Öffnen: 46 MB<br>
      Nach dem Schließen des Formulars: 46 MB<br>

      Beim ASP.Net verwende ich nur ADO.NET Datenprovider

      Comment


      • #4
        Hallo,

        &gt;Dieses Problem tritt bei einer VCL.NET-Anwendung auf. <br>
        &gt;Ich verwende nur managed Code!

        was nützt das, wenn VCL.NET am .NET Framework vorbei direkt aufs Win32 zugreift und intern das meiste von VCL.NET als unmanaged Code abläuft?

        &gt;Beim ASP.Net verwende ich nur ADO.NET Datenprovider!

        Wie sieht ein kurzes (!) Beispiel aus, mit dem sich dieser Effekt jederzeit reproduzieren lässt? Wie viele CPUs stecken im Server (IIS)? In welchem Modus wird der GC betrieben (siehe <i>.NET Framework Configuration | Eigenschaftn von My Computer | Radiobutton <b>Garbage collection mode</b></i>)

        Comment


        • #5
          &gt;was nützt das, wenn VCL.NET am .NET Framework vorbei direkt aufs Win32 zugreift und intern das meiste von VCL.NET als unmanaged Code abläuft?

          d.h. Der Garbage Collector arbeitet bei den ganzen VCL.NET-Komponenten nicht richtig? Muss ich meine ganzen Formulare und alle Objekte selber freigeben?

          &gt;In welchem Modus wird der GC betrieben (siehe .NET Framework Configuration | Eigenschaftn von My Computer | Radiobutton Garbage collection mode)?

          Garbage collection mode:
          "Für Benutzeranwendungen im Hintergrund ausführen"

          &gt;Wie sieht ein kurzes (!) Beispiel aus, mit dem sich dieser Effekt jederzeit reproduzieren lässt? Wie viele CPUs stecken im Server (IIS)?

          Dieser Effekt tritt aber nicht nur bei Datenbankzugriffen auf sondern auch bei anderen Seiten.
          Es steckte eine CPU im Server

          Comment


          • #6
            Hallo,

            &gt;..arbeitet bei den ganzen VCL.NET-Komponenten nicht richtig?

            Es wäre juristischer Selbstmord zu behaupten, dass <b>alle</b> VCL.NET-Komponenten an dieser Stelle ein Problem haben ;-)

            &gt;Muss ich meine ganzen Formulare und alle Objekte selber freigeben?

            Die Antwort auf diese Frage hängt mit beiden anderen Teilfragen zusammen. Wenn der Garbage Collector (GC) mit Hintergrund-Modus ("<i>Für Benutzeranwendungen im Hintergrund ausführen</i>") ausgeführt wird, läuft er unter Last in der Regel erst dann an, wenn eine neue Speicheranforderung nicht genug freien Speicherplatz vorfindet. Dann allerdings rattert die Festplatte erst einmal längere Zeit. Wenn man dieses Verhalten nicht möchte, muss man entweder den GC im Vordergrund betreiben oder freiwillig die nicht mehr benötigten (größeren) Objekte mit <b>Dispose</b> freigeben.

            Immer dann, wenn man auf ein zuerst nicht erklärbares Verhalten stößt, würde ich folgendes machen: <br>
            1. Zuerst in C# oder VB.NET nachprüfen, ob dort das gleiche Verhalten auftritt. <br>
            2. Wenn sich das Problem in C# oder VB.NET nicht reproduzieren lässt, das Ganze in Delphi 8 mit einer FCL-Anwendung (Windows Forms) nachprüfen. <br>
            3. Wenn sich in der Delphi 8-FCL-Anwendung das Problem nicht reproduzieren lässt, die VCL.NET-Anwendung solange abspecken, bis das Problem verschwindet. Im Worst Case verschwindet das Problem niemals :-(

            &gt;Es steckt eine CPU im Server

            In diesem Fall muss sich der GC den einzigen Prozessor mit dem eigentlichen Programm teilen. Für Serveranwendungen ist eine Mehrprozessor-Maschine (>= 2 CPUs) besser geeignet, da dann in der Voreinstellung der GC im Vordergrund auf der 2. CPU läuft.

            &gt;..Im Form werden Bilder geladen und angezeigt.

            Bei einem Test mit einem leeren VCL.NET-Formular, auf dem nur eine TTimer-Komponente liegt, ist kein Hochlaufen des Working Set des Prozesses zu beobachten (nach 100 Aufrufen ist das Working Set des Prozesses nur um 4 kByte größer geworden). Auch beim Grafik-Formular pendelt das Working Set zwischen 2 stabilen Grenzen. Allerdings lege ich aber "freiwillig" durch den <b>Relaese</b>-Aufruf den Zeitpunkt des Freigebens selbst fest, so dass der Speicherverbrauch erst gar nicht hochläuft:

            a) Hauptformular
            <pre>
            <b>unit</b> FormMain;
            <br>
            <b>interface</b>
            <br>
            <b>uses</b>
            Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
            Dialogs, Borland.Vcl.StdCtrls, System.ComponentModel,
            Borland.Vcl.ComCtrls;
            <br>
            <b>type</b>
            TForm1 = <b>class</b>(TForm)
            StatusBar1: TStatusBar;
            EditCount: TEdit;
            Label1: TLabel;
            ButtonFormEmpty: TButton;
            ButtonFormGrafik: TButton;
            <b>procedure</b> ButtonFormEmptyClick(Sender: TObject);
            <b>procedure</b> ButtonFormGrafikClick(Sender: TObject);
            <b>private</b>
            <font color="#003399"><i>{ Private-Deklarationen }</i></font>
            <b>public</b>
            <font color="#003399"><i>{ Public-Deklarationen }</i></font>
            <b>end</b>;
            <br>
            <b>var</b>
            Form1: TForm1;
            <br>
            <b>implementation</b>
            <br>
            <b>uses</b> FormEmpty, FormGrafik;
            <br>
            <font color="#003399"><i>{$R *.nfm}</i></font>
            <br>
            <b>procedure</b> TForm1.ButtonFormEmptyClick(Sender: TObject);
            <b>var</b>
            i, iMax : Integer;
            <b>begin</b>
            iMax := Convert.ToInt32(EditCount.Text);
            <b>for</b> i := 1 <b>to</b> iMax <b>do</b>
            <b>begin</b>
            Form2 := TForm2.Create(<b>nil</b>);
            <b>try</b>
            Form2.ShowModal;
            <b>finally</b>
            Form2.Release;
            <b>end</b>;
            StatusBar1.SimpleText := i.ToString;
            Application.ProcessMessages;
            <b>end</b>;
            <b>end</b>;
            <br>
            <b>procedure</b> TForm1.ButtonFormGrafikClick(Sender: TObject);
            <b>var</b>
            i, iMax : Integer;
            <b>begin</b>
            iMax := Convert.ToInt32(EditCount.Text);
            <b>for</b> i := 1 <b>to</b> iMax <b>do</b>
            <b>begin</b>
            Form3 := TForm3.Create(<b>nil</b>);
            <b>try</b>
            Form3.ShowModal;
            <b>finally</b>
            Form3.Release;
            <b>end</b>;
            StatusBar1.SimpleText := i.ToString;
            Application.ProcessMessages;
            <b>end</b>;
            <b>end</b>;
            <br>
            <b>end</b>.
            </pre>
            b) aufgerufenes leeres Formular
            <pre>
            <b>unit</b> FormEmpty;
            <br>
            <b>interface</b>
            <br>
            <b>uses</b>
            Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
            Dialogs, System.ComponentModel, Borland.Vcl.ExtCtrls;
            <br>
            <b>type</b>
            TForm2 = <b>class</b>(TForm)
            Timer1: TTimer;
            <b>procedure</b> Timer1Timer(Sender: TObject);
            <b>procedure</b> FormShow(Sender: TObject);
            <b>private</b>
            <font color="#003399"><i>{ Private-Deklarationen }</i></font>
            <b>public</b>
            <font color="#003399"><i>{ Public-Deklarationen }</i></font>
            <b>end</b>;
            <br>
            <b>var</b>
            Form2: TForm2;
            <br>
            <b>implementation</b>
            <br>
            <font color="#003399"><i>{$R *.nfm}</i></font>
            <br>
            <font color="#003399"><i>{ TForm2 }</i></font>
            <br>
            <b>procedure</b> TForm2.Timer1Timer(Sender: TObject);
            <b>begin</b>
            Close;
            <b>end</b>;
            <br>
            <b>procedure</b> TForm2.FormShow(Sender: TObject);
            <b>begin</b>
            Timer1.Interval := 100;
            Timer1.Enabled := True;
            <b>end</b>;
            <br>
            <b>end</b>.
            </pre>

            c) aufgerufenes Grafik-Formular lädt eine 385 kByte-Bitmap
            <pre>
            <b>unit</b> FormGrafik;
            <br>
            <b>interface</b>
            <br>
            <b>uses</b>
            Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
            Dialogs, Borland.Vcl.ExtCtrls, System.ComponentModel;
            <br>
            <b>type</b>
            TForm3 = <b>class</b>(TForm)
            Image1: TImage;
            Timer1: TTimer;
            <b>procedure</b> FormShow(Sender: TObject);
            <b>procedure</b> Timer1Timer(Sender: TObject);
            <b>private</b>
            <font color="#003399"><i>{ Private-Deklarationen }</i></font>
            <b>public</b>
            <font color="#003399"><i>{ Public-Deklarationen }</i></font>
            <b>end</b>;
            <br>
            <b>var</b>
            Form3: TForm3;
            <br>
            <b>implementation</b>
            <br>
            <font color="#003399"><i>{$R *.nfm}</i></font>
            <br>
            <b>procedure</b> TForm3.FormShow(Sender: TObject);
            <b>begin</b>
            Timer1.Interval := 100;
            Timer1.Enabled := True;
            Image1.Picture.LoadFromFile(<font color="#9933CC">'C:\Temp\D8TEST.bmp'</font>);
            <b>end</b>;
            <br>
            <b>procedure</b> TForm3.Timer1Timer(Sender: TObject);
            <b>begin</b>
            Timer1.Enabled := False;
            Close;
            <b>end</b>;
            <br>
            <b>end</b>.
            </pre>
            Wird der Aufruf von <b>Form3.Release</b> durch <b>Form3 := nil</b> ersetzt, ballert man sich das Working Set des Prozesses bei 100 Aufrufen des Grafik-Formulars um <b>70 MByte</b> zu. Es ist somit auch unter .NET in jedem Fall sinnvoll, derart große Objekte freiwillig explizit (und somit zu einem definierten Zeitpunkt) freizugeben, wenn man derartige Zuwächse nicht möchte.
            &#10

            Comment


            • #7
              &gt;Es wäre juristischer Selbstmord zu behaupten, dass alle VCL.NET-Komponenten an dieser Stelle ein Problem haben ;-)

              War ja nicht so gemeint *g*
              <br><br>
              Habe jetzt auch nochmals getestet. Ein Formular wird 50 mal aufgerufen und darin ein 783 KB großes Bitmap angezeigt und danach wieder geschlossen. Folgende Speicherauslastungen sind aufgetreten:
              ohne Freigabe bzw. beim zuweisen von <b>nil</b>:<br><br>
              83 / 83 MB mehr als vorm Aufruf(Speicherauslastung / Virtueller Speicher; lt. Taskmanager)
              <br>
              Bei Aufruf von Dispose:
              9 / 8 MB mehr Speicherauslastung
              <br>
              Bei Aufruf von Release:
              2 / 1 MB mehr Speicherauslastung
              <br><br>
              Ich bin das von Java so gewohnt dass ich die Objekte nur auf <b>null</b> setzen muss. Aber ich werde ab sofort große Objekte explizit freigeben.<br>
              Bei einem weiteren Versuch habe ich die Formulare nicht freigegeben und danach explizit den Garbage-collector aufgerufen (<b>GC.Collect</b>). Aber der Speicher wurde trotzdem nicht freigegeben. Deshalb muss ich annehmen dass der Garbage-Collector nicht richtig funktioniert.<br><br>
              Weiters habe ich entdeckt dass wenn ich die das Formular minimiere, der Prozess nur noch 1 MB Speicher benötigt. D.h. der Speicher wird dann doch freigegeben (der Virtuelle Speicher ist noch immer gleich)

              Comment


              • #8
                Ich habe jetzt noch einen Test mit WinForms gemacht:
                <pre>
                procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
                var
                imageform: TWinForm1;
                i: Integer;
                begin
                for i:=0 to 50 do
                begin
                imageform:=TWinForm1.Create;
                imageform.Show;
                end;
                end;
                </pre>
                Hier gebe ich die Formulare nicht explizit frei. Folgende Speicherauslastung ergibt sich (Speicherauslastung / Virtueller Speicher; lt. Taskmanager):<br>
                19,5 / 10,6 MB beim Starten des Programms<br>
                100,9 / 51,3 MB nach der for-Schleife<br>
                20,8 / 11,1 MB nach dem aufruf des Garbage-Collectors<br><br>
                Heißt das ich kann den Garbage-Collector bei VCL.NET-Anwendungen vergessen und muss alles händisch freigeben

                Comment


                • #9
                  Hallo,

                  bei einer FCL-Anwendung oder einer C#- bzw. VB.NET-Anwendung ist die Sache einfach - das Programm greift direkt auf die Klassen/Komponenten aus dem .NET-Framework zu, so dass der GC ständig "im Bilde" ist (oder technischer gesagt, seine Listen mit den Objektreferenzen sind akkurat gefüllt).

                  Die VCL.NET ist <b>kein</b> Aufsatz auf die FCL (.NET Framework Class Library), sondern greift auf einem Nebenweg direkt auf Win32 zu (das ist ein Unterschied gegenüber früher, wo die VCL nur ein Aufsatz auf das Win32 war). Somit muss man in der Tat ständig damit rechnen, dass der GC nicht immer feststellen kann, ob eine Objektinstanz noch gebraucht wird. Denn der GC räumt eine Instanz erst dann ab, wenn keine andere Programmstelle darauf eine Referenz offen hält

                  Comment


                  • #10
                    Hallo.

                    Dann kann ich ja einen sehr großen Vorteil von .NET mit VCL.NET (automatische Speicherverwaltung bwz. -freigabe) nicht wirklich benutzen und muss alles immer explizit freigeben. Damit laufe ich ja Gefahr große Speicherleaks zu erzeugen.<br><br>
                    Da wäre es ja besser auf Delphi 7 zu bleiben (stabiler und schneller) oder unter Delphi WinForms zu programmieren oder überhaupt gleich C# oder VB.Net zu verwenden.<br><br>
                    Ist es möglich dass bei Delphi 9 VCL.NET auf die FCL aufsetzt, oder wird dieser eigenständige Weg weitergeführt

                    Comment


                    • #11
                      Hallo,

                      &gt;Ist es möglich dass bei Delphi 9 VCL.NET auf die FCL aufsetzt...

                      Nein - dazu sind die Unterschiede zu groß. Außerdem hebt ja das Borland-Marketing die VCL.NET als Alleinstellungsmerkmal besonders hervor, da nur die VCL.NET auch zukünftig für eine enge Kundenbindung sorgt.

                      &gt;...oder überhaupt gleich C# oder VB.Net zu verwenden.

                      Es eher eine "Sowohl-als-auch"-Entscheidung. Ich greife seit längerem für eine bestimmte Aufgabe immer zu dem Tool (Delphi 7, Delphi 8 oder VS.NET 2003), das für dieses konkrete Aufgabe am Besten geeignet ist. Bei meinen beiden letzten Projekten (Three-Tier-Datenbankanwendungen, die bundesweit über das WAN auf eine zentrale Datenbank zugreifen) besteht der COM+ Serverteil aus einem <i>.NET Enterprise Service</i>, der mit VS.NET 2003 in C# geschrieben wurde, aber die Clients sind noch Delphi 7 (Win32), die über DCOM auf den .NET Enterprise Service zugreifen.

                      Gerade die Wizards von VS.NET sind zum Beispiel im Datenbankbereich (falls der MS SQL Server verwendet wird) um Größenordnungen leistungsfähige als bei der sonstigen Konkurrenz. In diesem Zusammenhang macht es selbstverständlich nur dann Sinn, wenn auch in Delphi 8 nur die FCL genutzt wird, da dann alle Sprachen die gleiche Klassenbibliothek nutzen. Und da eine Anwendung in verschiedenen Sprachen geschrieben werden darf, sammelt sich im Laufe der Zeit eine eigene Tool-Sammlung in Gestalt von Assembly-DLLs an, die dann jederzeit von beliebigen Projekten eingebunden werden kann

                      Comment


                      • #12
                        Für ein ganz neues Projekt ist es also am sinnvollsten unter FCL zu programmieren, da die VCL.NET eigentlich nur beim portieren bestehender Anwendungen Vorteile bringt.

                        Ich habe für ein neues Projekt schon einiges in VCL.NET programmiert und komme jetzt immer mehr auf Fehler im Delphi 8 und auf Einschränkungen drauf. Zurzeit überlege ich ob ich nicht auf Delphi 8-WinForm oder auf C# umsteigen soll, da mir das Visual Studio 2003 sehr stabil scheint

                        Comment

                        Working...
                        X