frage zur garbagecollection



  • hi, ich bin relativ neu in c#, und habe daher eine verständnisfrage zum thema garbage collector.
    ich habe mir ein testproggie gecodet, in der statischen Methode "Bla()" ein objekt "MyApp" erzeugt - und der Konstruktor von "MyApp" erzeugt nun ein objekt der Klasse "Test".
    Nun würde mich interessieren, wann jetzt das "MyApp" objekt freigegeben wird, weil ich habe das doch (wenn ich mich nicht irre) in der Methode "Bla" LOKAL erzeugt - oder nicht ????
    ich komme beim debuggen NIE in einen Konstruktor der 2 Klassen.
    übrigens das while(true) iss nur drinnen, das das proggie weiterläuft.

    Noch ne Frage - wie kann ich das "MyApp" objekt manuell freigeben ??

    danke !

    using System;
    
    namespace Project3
    {
    	public class Test
    	{
    	int i;
    	public Test()
    	{
    		i=0;
    	}
    		~Test()
    		{
    			i=0;
    		}
    	}
    	public class MyApp
    	{
    		private Test test;
    		public static void Bla()
    		{
    			MyApp app=new MyApp();
    		}
    		public static void Main()
    		{
    			Bla();
    			while(true);
    		}
    
    		public MyApp()
    		{
    			test=new Test();
    		}
    		~MyApp()
    		{
    			test=null;
    		}
    
    	}
    }
    


  • so, ich habe nun folgende änderung vorgenommen, und siehe da der Konstruktor von meiner "Test" Klasse wird nun aufgerufen.
    ABER: zu was habe ich eine automatische Garbage Collection, wenn ich mich dann doch selbst ums "ausmisten" kümmern muss ??? bzw. WANN wird die dann automatisch aufgerufen und genügt es nicht einfach test auf null zu setzten ????

    public MyApp()
    {
        test=new Test();
        test=null;
        System.GC.Collect();
    }
    


  • krampfader schrieb:

    so, ich habe nun folgende änderung vorgenommen, und siehe da der Konstruktor von meiner "Test" Klasse wird nun aufgerufen.

    Du meinst bestimmt den Destruktor.

    ABER: zu was habe ich eine automatische Garbage Collection, wenn ich mich dann doch selbst ums "ausmisten" kümmern muss ???

    Du kümmerst dich schon viel zu viel darum ... lass den GC doch machen. Der räumt das Objekt weg, wenn es ihm gerade passt. Also spätestens, wenn der Speicher voll ist, und du noch mehr Objekte erzeugen willst. Vielleicht auch nie, wozu auch (BTW ist das Spekulation meinerseits, ich kenne .net nicht genau genug, bei Java ist es jedenfals unbestimmt, ob Finalizer am Programmende aufgerufen werden).

    bzw. WANN wird die dann automatisch aufgerufen und genügt es nicht einfach test auf null zu setzten ????

    Das würde genügen, wenn .net mit Referenzzählung arbeiten würde. Dh bei jedem Ändern einer Referenz werden die Referenzzähler aller beteiligen Objekte angepasst. Geht ein Zähler auf 0, wird das Objekt zerstört. So arbeiten einige Smart-Pointer in C++, in Python und Perl wird das auch angewandt.
    .net wird eine Variante des Mark&Sweep-Algorithmus verwenden, d.h. dass zu bestimmten Zeitpunkten der GC anläuft und alle Objekte sucht, die noch erreichbar sind, und diese markiert. Alle nicht erreichbaren Objekte werden zerstört (wenn sie keinen Finalizer haben, können sie einfach "vergessen" werden). Daraus folgt IMHO, dass Finalizer möglichst vermieden werden sollten, weil sie den GC behindern.



  • ja, ich hab das mit dem konstruktor und destruktor verknödelt, sorry.
    mir gehts in erster linie darum, objekt die ich nicht mehr benötige auch selbst entfernen zu können.
    Aber ich denke GC.Collect() is net so optimal (lt. C# kompendium), und ich blick da net ganz durch....

    Mein Ziel iss es, objekte in eine Hashtable oder Arraylist einzufügen.
    Wenn ich dann ein objekt aus der Hashtable bzw. liste entferne, möchte ich dass dieses dann auch (wenn möglich) destruktiert wird.

    using System;
    using System.Collections;
    
    //***********************************************************************************
    // listentry class
    //***********************************************************************************
    class MyListEntry
    {
    	private int nTestEntry;
    	//-------------------------------------------------------------------------------
    	// konstr.
    	public MyListEntry()
    	{
    		nTestEntry=0;
    	}
    	//-------------------------------------------------------------------------------
    	// destr.
    	~MyListEntry()
    	{
    		nTestEntry=0;
    	}
    	//-------------------------------------------------------------------------------
    	// irgendwas.......
    	public  int TestEntry
    	{
    		set
    		{
    			nTestEntry=value;
    		}
    		get
    		{
    			return nTestEntry;
    		}
    	}
    }
    //***********************************************************************************
    // myproggie class
    //***********************************************************************************
    class MyProggie
    {
    	private Hashtable ht;
    	//-------------------------------------------------------------------------------
    	// konstr.
    	public MyProggie()
    	{
    		ht=new Hashtable();
    	}
    	//-------------------------------------------------------------------------------
    	// destr.
    	~MyProggie()
    	{
    		ht.Clear();
    	}
    	//-------------------------------------------------------------------------------
    	// objekt in hashtable einfügen
    	public bool Add(object objID,MyListEntry ml)
    	{
    		try
    		{
    			if(ht!=null)
    			{
    				ht.Add(objID,ml);
    			}
    		}
    		catch
    		{
    		}
    		return false;
    	}
    	//-------------------------------------------------------------------------------
    	//objekt aus hashtable rausnehmen
    	public bool Remove(object objID)
    	{
    		try
    		{
    			if(ht.ContainsKey(objID))
    			{
    				ht.Remove(objID);
    				return true;
    			}
    		}
    		catch
    		{
    		}
    		return false;
    	}
    }
    //***********************************************************************************
    // MAIN 
    //***********************************************************************************
    class MyApp
    {
    	static void Main()
    	{
    		MyProggie prog = new MyProggie();
    		// objekt in hashtable reinmachen
    		prog.Add("EINS",new MyListEntry());
    		// objekt aus hashtable rausnehmen
    		prog.Remove("EINS"); // anschliessend soll die Garbage Collection das ding wegräumen
    		GC.Collect();		 // wenn diese zeile drinnen iss, funzt das ganze - aber is das das gelbe vom ei ??
    
    		while(true);
    	}
    }
    
    //***********************************************************************************
    //***********************************************************************************
    //***********************************************************************************
    


  • krampfader schrieb:

    Wenn ich dann ein objekt aus der Hashtable bzw. liste entferne, möchte ich dass dieses dann auch (wenn möglich) destruktiert wird.

    Warum? Hat das einen konkreten Grund, oder hältst du es einfach für stilvoller o.ä.?



  • ich bin umsteiger aus der "c++-ecke" und daher wäre es mir lieber, objekte die ich nicht mehr brauche zu entfernen.
    Die einträge in meine hashtables sind directx-soundbuffers und texturen und so zeugs.
    da dieses resourcensystem in einer dll steckt und von mehreren exe files benutzt wird - welche natürlichst wieder verschiedene sachen anfordern, und ggf. nicht benutzes zeugs rausschmeissen sollen/können - wollte ich ganz sicher gehen, dass ich mir den speicher zur laufzeit "nicht zumülle" - und so wegen "stilvoller" eben 🤡

    ich "vertrau" nun mal dem GC - wird sich schon zeigen was er macht !

    Danke !



  • Du musst umdenken. Der Heap geht dich nichts mehr an. Der wird per mark & compact automatisch bereinigt und defragmentiert.

    Und "stilvoller" ist überhaupt kein Argument. 🙂 Es ist in C# absolut normal, Objekte mit new zu erstellen und sie irgendwann einfach nicht mehr zu benutzen. Das ist auch nicht schlampig oder sonstirgendwas, sondern in den meisten Fällen sehr effizient.
    Da der Heap immer defragmentiert ist, muss zum erstellen eines neuen Objekts (ähnlich wie beim Stack) nur ein Zeiger erhöht werden. Wenn der GC anläuft könntest du evtl. ganz kurz einen Hänger haben, aber nur wenn der GC nicht viel freigeben kann, weil er nur lebende Objekte verschiebt.

    Kurzum: Sorge dich nicht, lebe. 🙂



  • ~MyListEntry() 
        { 
            nTestEntry=0; 
        }
    

    Was für einen Sinn macht dieser Destruktor? Lass ihn doch bitte einfach weg.



  • Hallo,
    ich hätte da auch noch ein paar Frage zu dem GC bzw. der
    Speicherfreigabe in C#.

    In einem konkreten Projekt lade ich in eine PictureBox eine
    von mir erstellte Bitmap.
    dies sieht ungefähr so aus.

    Bitmap bmp = new Bitmap ( 800, 600);
    Graphics graphics = Graphics.FromImage( bmp );
    graphics.Clear ( Color.Red);
    myPictureBox.Image = bmp;
    

    Wenn ich dies nun öfter in dem Programm mache, sehe ich
    im Taskmanager wie der Speicher bis zum Anschlag belegt wird.
    Und bei etwa 180 MB, bekomme ich dann eine Systemnachricht,
    das nicht mehr genügend virtuellen Speicher zur Verfügung steht
    und mein Programm stürzt ab.

    Meine Fragen wären nun:
    Was mache ich falsch?
    Wie könnte ich es besser machen?

    Links, SuchBegriffe in der MSDN etc. würden mir auch weiterhelfen.



  • [Theorie]

    Das Bitmap-Objekt verwaltet seinen Speicher selbst und gibt ihn erst frei, wenn es zerstört wurde. Es wird jedoch nie vom GC aufgefressen, weil der in seinem Heap noch genügend Platz hat (das Bitmap-Objekt selber ist ja nicht groß, nur der Speicher, den es noch belegt) und deshalb nicht aktiv wird.

    Solche Klassen implementieren deshalb das Interface IDisposable (heisst das so?), damit man sie manuell über die Methode Dispose() zerstören kann, oder zumindest das Objekt auffordern kann, seinen Speicher freizugeben.

    Wohlgemerkt, der GC würde das schon machen, aber erst wenn IHM der Speicher ausgeht. Er weiss ja nicht, was das Bitmap Objekt sonst noch für Sachen macht.

    [/Theorie]

    Am besten, du machst dich mal über die verwendete Bitmap-Klasse schlau. Du kannst auch den GC manuell aufrufen, wenn du weisst, dass du ab jetzt die Bitmap Objekte nicht mehr referenzierst. Das machst du mit System.GC.Collect() oder mit System.GC.Collect(generation).
    Übrigens ist mir nicht klar, was

    Graphics graphics = Graphics.FromImage( bmp );
    graphics.Clear ( Color.Red);
    

    diese zwei Zeilen bezwecken sollen.



  • ±ƒ§Ä{£­© (Müll)



  • krampfader,

    krampfader schrieb:

    Wenn ich dann ein objekt aus der Hashtable bzw. liste entferne, möchte ich dass dieses dann auch (wenn möglich) destruktiert wird.

    Du kannst grundsätzlich keine Annahmen über den Zeitpunkt der Zerstörung eines Objektes machen - selbst dann nicht, wenn Du explizit GC.Collect() aufrufst. Ob ein Objekt zerstört wird, hängt im wesentlichen von seiner Langlebigkeit ab. Und noch etwas ist in diesem Zusammenhang wichtg: wenn Du selbst eine Finalisierungs-Methode schreibst (~MyListEntry), führt das dazu, daß das Objekt erst eine Speicherbereinigung später durch den GC zerstört wird, da bei der ersten Bereinigung die Finalisierungs-Methode aufgerufen werden muß und das Objekt erst beim nächsten Durchlauf in der Liste der anderen "normalen" zu zerstörenden Objekte landet.

    Bashar schrieb:

    bei Java ist es jedenfals unbestimmt, ob Finalizer am Programmende aufgerufen werden

    ...werden sie bei DotNet nicht, glaube ich. Man kann das Aufrufen zwar mit System.GC.RequestFinalizeOnShutdown() erzwingen. Das kostet dann aber etwas mehr Zeit beim Beenden des Programmes.

    CSharpCoder schrieb:

    Wenn ich dies nun öfter in dem Programm mache, sehe ich
    im Taskmanager wie der Speicher bis zum Anschlag belegt wird.

    Der Thread, in dem der GC läuft, versucht bei günstigen Gelegenheiten Speicherbereinigungen auszuführen - also z.B. dann, wenn das Program gerade auf eine Nutzer-Eingabe wartet. Wenn Du also sehr schnell immer wieder neue Objekte erzeugst gibst Du dem GC weniger Möglichkeiten dazu und es entsteht eine Art Wettbewerb: Wer ist schneller, der GC mit dem Zerstören oder Du mit dem Neuerstellen? Wenn Du also genau weißt, daß Du massiv neue und kurzlebige Objekte erzeugst, bietet sich das explizite Aufrufen von GC.Collect() an, wann immer Du das für sinnvoll erachtest. Sind die Objekte kurzlebig, ist die Wahrscheinlichkeit hoch, daß Sie bei der Bereinigung tatsächlich zerstört werden. Wenn diese Objekte langlebig sind, mußt Du Dir halt mehr Speicher kaufen ;).
    ----- Edit -----
    Es gibt dazu ein interessantes Kapitel in einem guten c# Online-Buch.



  • Der Thread, in dem der GC läuft, versucht bei günstigen Gelegenheiten Speicherbereinigungen auszuführen - also z.B. dann, wenn das Program gerade auf eine Nutzer-Eingabe wartet. Wenn Du also sehr schnell immer wieder neue Objekte erzeugst gibst Du dem GC weniger Möglichkeiten dazu und es entsteht eine Art Wettbewerb: Wer ist schneller, der GC mit dem Zerstören oder Du mit dem Neuerstellen? Wenn Du also genau weißt, daß Du massiv neue und kurzlebige Objekte erzeugst, bietet sich das explizite Aufrufen von GC.Collect() an, wann immer Du das für sinnvoll erachtest. Sind die Objekte kurzlebig, ist die Wahrscheinlichkeit hoch, daß Sie bei der Bereinigung tatsächlich zerstört werden.

    Das stimmt so nicht. Die Reinigung wird genau dann durchgeführt, wenn kein neues Objekt allokiert werden kann oder wenn du es anforderst.
    Es entsteht auch kein Wettrennen. Während der Reinigung wird der Heap umgeschichtet und deshalb kann kein Thread solange auf Objekte zugreifen oder gar neue erstellen.
    Und Lang- bzw. Kurzlebigkeit wird nicht über die Zeit definiert sondern über die Generationen, also wie oft ein Objekt eine Reinigung überlebt hat. Objekte, die die erste Reinigung überleben haben eine gute Chance noch viel länger zu leben, weil sie in den meisten Fällen dann nicht mehr betrachtet werden. Der GC geht nämlich davon aus, dass die meisten Objekte nur sehr kurz existieren und die, die eine Reinigung überleben, sehr lange existieren.

    Ein Aufruf von GC.Collect() ist dann sinnvoll, wenn man gerade eine Menge Objektreferenzen aufgibt und es gerade nicht so schlimm ist, wenn das Programm für ein paar Sekundenbruchteile nicht reagiert. Ich könnte es mir z.B. bei einem Spiel vorstellen, wenn ein Spiel verlassen wird und man ins Hauptmenü zurückkehrt, dass man dann ne Reinigung durchführt, weil es im Hauptmenü schon mal kurz hängen darf.



  • <OT>
    @Optimizer: Schau endlich mal ins NADRW-Forum 😉
    </OT>

    MfG SideWinder





  • Optimizer schrieb:

    Der Thread, in dem der GC läuft, versucht bei günstigen Gelegenheiten Speicherbereinigungen auszuführen - also z.B. dann, wenn das Program gerade auf eine Nutzer-Eingabe wartet.

    Das stimmt so nicht. Die Reinigung wird genau dann durchgeführt, wenn kein neues Objekt allokiert werden kann oder wenn du es anforderst.

    Hmm, ja, das ist erst mal richtig:

    MSDN schrieb:

    Tatsächlich wird eine Garbage Collection erst dann eingeleitet, wenn der Bereich von Generation 0 voll ist.

    Das ist vermutlich jedoch nur die halbe Wahrheit. Die Garbage Collector Strategien ändern sich praktisch von Version zu Version.
    Hier z.B. findet sich folgendes:

    MSDN schrieb:

    When Does a Collection Happen?
    When a time allocation is made, the GC checks to see if a collection is needed.
    It looks at the size of the collection, the amount of memory remaining, and the sizes
    of each generation, and then uses a heuristic to make the decision.

    Die Details sind leider alle undokumentiert ...

    Optimizer schrieb:

    Es entsteht auch kein Wettrennen. Während der Reinigung wird der Heap umgeschichtet und deshalb kann kein Thread solange auf Objekte zugreifen oder gar neue erstellen.

    Natürlich werden während der Garbage Colection alle anderen Threads angehalten. Mit "Wettrennen" meinte ich, daß das laufende Programm und der GC abwechselnd Objekte erzeugen und zerstören können. Abgesehen davon können auch noch andere, möglicherweise interne, Threads dazu führen, daß die Garbage Collection angestoßen wird.

    Optimizer schrieb:

    Und Lang- bzw. Kurzlebigkeit wird nicht über die Zeit definiert sondern über die Generationen, also wie oft ein Objekt eine Reinigung überlebt hat.

    Von Zeit war nicht die Rede. Die Lebensdauer eines Objektes wird in erster Linie durch die Verweise bestimmt, die das jeweilige Programm auf das Objekt hält. Als Folge davon ordnet der GC die Objekte den verschiedenen Generationen zu.



  • Mit "Wettrennen" meinte ich, daß das laufende Programm und der GC abwechselnd Objekte erzeugen und zerstören können.

    Daraus kann ich nicht auf ein Wettrennen schließen, außerdem hast du geschrieben:

    Wer ist schneller, der GC mit dem Zerstören oder Du mit dem Neuerstellen?

    Was dann schon ziemlich missverständlich war.

    Die Lebensdauer eines Objektes wird in erster Linie durch die Verweise bestimmt, die das jeweilige Programm auf das Objekt hält. Als Folge davon ordnet der GC die Objekte den verschiedenen Generationen zu.

    Nein das ist so nicht ganz richtig. 🙂 Jedes Objekt startet in Generation 0 und nach einer Reinigung wird der "Generation-1-Zeiger" (nach der Kompaktierung des Heaps) hinter das letzte noch lebende Objekt gesetzt. dito für Generation 2.
    Die Verweise auf das Objekt haben mit den Generationen nichts zu tun, eine Generation ist einfach ein Speicherbereich aus dem Heap, der bei einer Reinigung eben betrachtet wird oder nicht.

    Mit dem Punkt, wann die Reinigung durchgeführt wird, scheinst du allerdings Recht zu haben. In der Praxis gibt es sicherlich einige schlaue Überlegungen, die viele Faktoren berücksichtigen, um zu einem günstigen Zeitpunkt die Reinigung durchzuführen, da darf man sich nicht so streng an die Theorie halten.



  • Hi,

    Optimizer schrieb:

    Daraus kann ich nicht auf ein Wettrennen schließen...

    hack' doch bitte nicht so auf diesem Wort herum. Das sollte bloß so eine Art didaktischer Kunstgriff sein :).

    Optimizer schrieb:

    Nein das ist so nicht ganz richtig. 🙂 Jedes Objekt startet in Generation 0 ...

    Ja schon, aber was hat das mit meinem Beitrag zu tun. Das habe ich nicht in Zweifel gezogen. Mir ging es um die Frage nach Ursache und Wirkung - du schreibst:

    Optimizer schrieb:

    Und Lang- bzw. Kurzlebigkeit wird nicht über die Zeit definiert sondern über die Generationen, also wie oft ein Objekt eine Reinigung überlebt hat.

    Das ist so, als würde ich sagen: Es muß wohl Nacht sein, da es dunkel ist. Es kann aber auch sein, daß irgend ein Spaßvogel mir eine Tüte über den Kopf gezogen hat. Die Ursache für die Einordnung der Objekte in die Generationen ist die Lebensdauer des Objektes innerhalb der Applikation ...

    Optimizer schrieb:

    Die Verweise auf das Objekt haben mit den Generationen nichts zu tun

    Das könnte ein Mißverständnis sein. Ich sprach von Verweisen, die das Programm auf die Objekte hält und die sind, wie gerade schon erwähnt, maßgeblich dafür verantwortlich, daß ein Objekt der Generation 0 eine Garbage Collection übersteht und in die nächste Generation verschoben wird. Das ist IMHO unstrittig. Die MSDN bezeichnet solche Objekte häufig als "erreichbare" Objekte.



  • Egal, wir meinen wohl beide das selbe. 🙂



  • Na gut.


Anmelden zum Antworten