none
Speicher einer Klasse wieder freigeben RRS feed

  • Frage

  • Hallo,

    ich programmiere jetzt schon eine Zeit lang mit C# .NET und starte jetzt mein erstes größeres Projekt - habe aber immer noch irgendwie nicht richtig verstanden  wie ich den Speicher wieder vernünftig wieder frei geben kann...

    Beispiel:

    Ich habe z.B. eine Klasse:

    public class person
    {
    	public string name {get;set;}
    	public string nachname {get;set;}
    	public int alter {get;set;}
    	
    }
    
    //############################################################
    
    public class myClass
    {
    	private List<person> _myList = new List<person>();
    	
    	
    	public List<person> holePersonen()
    	{
    		return _myList;
    	}
    	
    	
    	bool disposed = false;
    
    	// Public implementation of Dispose pattern callable by consumers.
    	public void Dispose()
    		{
    		Dispose(true);
    		GC.SuppressFinalize(this);
    		}
    
    	// Protected implementation of Dispose pattern.
    	protected virtual void Dispose(bool disposing)
    		{
    		if (disposed)
    			return;
    
    		if (disposing)
    			{
    			// Free any other managed objects here.
    			//
    			
    			connection.Dispose();
    			}
    
    
    		disposed = true;
    		}
    
    	~dbHandler()
    		{
    		Dispose(false);
    		}
    }

    Wie kann ich jetzt den Speicher der Liste freigeben?

    Mit "null" lösche ich ja nur die Referenz. Leider finde ich im Internet kein Vernünftiges Beispiel für selbst erstellte Klassen - immer nur das obige mit den Kommentaren.

    Würde mich freuen, wenn mir das jemand erklären könnte!

    Viele Grüße

    Freitag, 6. November 2015 22:36

Antworten

  • Hi,
    für die Speicherfreigabe im .NET ist der Garbage Collector verantwortlich. Er wird automatisch im Hintergrund gestartet, wenn dafür Bedarf und/oder CPU-Zeit zur Verfügung steht. Er durchläuft alle verwalteten Objekte im Heap (Speicherbereich, in welchem sich die Objekte befinden) und erkennt, ob es noch Verweise auf ein Objekt gibt. Wenn keine Verweise mehr vorhanden sind, gibt er den belegten Speicherplatz frei und verdichtet ggf. noch den Speicher. Verwaltete Objekt sind Objekte, die mit dem Framework erstellt wurden. Unverwaltete Objekte sind Objekte außerhalb der Hoheit des Frameworks, wie beispielsweise Dateizugriffe. Diese Objekte sollten explizit im Code (z.B. im Dispose) freigegeben werden.

    In Deinem konkreten Beispiel wird die Liste so lange nicht freigegeben, solange das Objekt vom Typ myClass existiert, da Du innerhalb des Objektes den Verweis, der in der Variablem _myList gehalten wird, nicht löschst (kein _myList=null; wird ausgeführt). Diese Anweisung könntest Du im Dispose einbauen. Das bringt aber wenig, da das Objekt vom Typ myClass nach dem Dispose sowieso nicht mehr brauchbar ist, was typischerweise mit einer Freigabe des Objektes verbunden ist bzw. sein sollte (z.B. using-Scope). Sobald Dein Objekt vom Typ myClass freigegeben wurde (es existiert kein Verweis mehr), wird auch im gleichen Durchlauf der Speicherplatz der Liste freigegeben.

    Wenn Du explizit den Durchlauf des Garbage Collectors erzwingen willst, dann rufe GC.Collect auf. Mit diesem Aufruf kann es aber passieren, dass Dein Programm ausgebremst wird. Deshalb sollte man GC.Collect nur aufrufen, wenn es dafür gewichtige Gründe gibt. Das kann z.B. sein, wenn Du mit umfangreichen Objektverkettungen oder Arrays viel Speicherplatz belegt hast, diesen nicht mehr benötigst und vom Programmablauf ein großer Speicherbedarf erwartet wird. Mit der voraus eilenden Freigabe steht dann genügend Speicherplatz bereit und muss nicht erst durch einen Lauf des Garbage Collector organisiert werden.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    • Als Antwort markiert PHep Samstag, 7. November 2015 23:33
    Samstag, 7. November 2015 04:12
  • Hi,
    beim Dispose wird der belegte Speicher erst nur zur Freigabe "markiert" und das Objekt kann deshalb nicht mehr benutzt werden. Zur weiteren Verwendung freigegeben wird der ehemals belegte Speicher IMMER durch den Garbage Collector. Mit GC.SuppressFinalize(this); wird dem Garbage Collector mitgeteilt, dass er den Finalizer nicht mehr aufzurufen braucht, bevor er den Speicherplatz endgültig freigibt. Damit wird eine (geringfügige) Beschleunigung der Arbeit des Garbage Collectors erreicht, was bei vielen Objekten ins Gewicht fallen kann. Wenn kein SupressFinalize aufgerufen wird, dann ruft der Garbage Collector vor der Freigabe des Speicherplatzes den Finalizer auf.

    Zur zweiten Frage ist zu bemerken, dass mit List<person>myList = new List<person>(); im Rumpf der Klasse der Speicherplatz für die Liste bereits zum Zeitpunkt der Instanziierung des Objektes belegt wird, d.h. beim myClass newClass = newmyClass();. Wenn nur die Variable für die Liste deklariert wird, wird bei der Instanziierung nur der Speicherplatz für den Verweis auf die Liste im Objekt reserviert (4 oder 8 Byte). Im Methodenaufruf wird dann der Speicherplatz für das Listenobjekt belegt. Damit kann die Belegung des Speicherplatzes verzögert werden. Ein Listenobjekt ohne Elemente belegt aber so wenig Speicherplatz, dass der Zeitpunkt der Instanziierung des Listenobjektes praktisch ohne Bedeutung ist. Vorrangig sollte der Algorithmus sein.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    • Als Antwort markiert PHep Montag, 9. November 2015 22:40
    Sonntag, 8. November 2015 04:48
  • Hallo,

    ja, in Dispose wird alles frei gegeben was der GC nicht hin bekommt. Das heißt, dass Klassen ohne IDisposable komplett vom GC entfernt werden. Wenn du selbst nachhelfen musst (besonders bei nativen Dingen wie Streams etc.) musst du das entsprechend selbst in die Disposed-Methode rein packen.

    Zu deiner Klasse: Zunächst mal solltest du die Anfangsbuchstaben von allem öffentlichen groß schreiben.
    Wie du die nun verwendest hängt von deinem Fall ab. Wenn du die Liste nur innerhalb der Methode verwenden willst, dann Variante 1 - ansonsten Variante 2.

    Am besten merkst du dir am besten alle Variablen in einem möglichst kleinen Raum zu deklarieren, das heißt wenn nur eine Methode die Variable braucht, dann deklarierst du sie in der Methode. Wenn dagegen mehrere Elemente die Variable brauchen, dann in der Klasse(/Typ).


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    • Als Antwort markiert PHep Samstag, 7. November 2015 23:33
    Samstag, 7. November 2015 21:25
    Moderator

Alle Antworten

  • Hi,
    für die Speicherfreigabe im .NET ist der Garbage Collector verantwortlich. Er wird automatisch im Hintergrund gestartet, wenn dafür Bedarf und/oder CPU-Zeit zur Verfügung steht. Er durchläuft alle verwalteten Objekte im Heap (Speicherbereich, in welchem sich die Objekte befinden) und erkennt, ob es noch Verweise auf ein Objekt gibt. Wenn keine Verweise mehr vorhanden sind, gibt er den belegten Speicherplatz frei und verdichtet ggf. noch den Speicher. Verwaltete Objekt sind Objekte, die mit dem Framework erstellt wurden. Unverwaltete Objekte sind Objekte außerhalb der Hoheit des Frameworks, wie beispielsweise Dateizugriffe. Diese Objekte sollten explizit im Code (z.B. im Dispose) freigegeben werden.

    In Deinem konkreten Beispiel wird die Liste so lange nicht freigegeben, solange das Objekt vom Typ myClass existiert, da Du innerhalb des Objektes den Verweis, der in der Variablem _myList gehalten wird, nicht löschst (kein _myList=null; wird ausgeführt). Diese Anweisung könntest Du im Dispose einbauen. Das bringt aber wenig, da das Objekt vom Typ myClass nach dem Dispose sowieso nicht mehr brauchbar ist, was typischerweise mit einer Freigabe des Objektes verbunden ist bzw. sein sollte (z.B. using-Scope). Sobald Dein Objekt vom Typ myClass freigegeben wurde (es existiert kein Verweis mehr), wird auch im gleichen Durchlauf der Speicherplatz der Liste freigegeben.

    Wenn Du explizit den Durchlauf des Garbage Collectors erzwingen willst, dann rufe GC.Collect auf. Mit diesem Aufruf kann es aber passieren, dass Dein Programm ausgebremst wird. Deshalb sollte man GC.Collect nur aufrufen, wenn es dafür gewichtige Gründe gibt. Das kann z.B. sein, wenn Du mit umfangreichen Objektverkettungen oder Arrays viel Speicherplatz belegt hast, diesen nicht mehr benötigst und vom Programmablauf ein großer Speicherbedarf erwartet wird. Mit der voraus eilenden Freigabe steht dann genügend Speicherplatz bereit und muss nicht erst durch einen Lauf des Garbage Collector organisiert werden.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    • Als Antwort markiert PHep Samstag, 7. November 2015 23:33
    Samstag, 7. November 2015 04:12
  • Danke schon mal...

    Also wird beim Aufruf von Dispose der komplette Speicher der Klasse freigegeben?

    Also z.B. so oder mit dem using:

    myClass newClass = new myClass();
    
    //Do something
    
    newClass.Dispose();

    Und noch eine Frage indirekt dazu...

    Wenn ich diese Klasse habe:

    public class person
    {
            public string name {get;set;}
    	public string nachname {get;set;}
    	public int alter {get;set;}
    	
    }

    Wie wird die am besten Verwendet?

    So?

    Version #1 

    public class myClass
    {
    
    public void firstmethod()
       {
          List<person> myList = new List<person>();
         
          //Do something with myList
       }
    
    }

    Oder so?

    Version #2

    public class myClass
    {
    List<person> myList;
    
    
    public void firstmethod()
       {
          myList = new List<person>();
         
          //Do something with myList
       }
    
    }

    Macht das überhaupt einen Unterschied? Oder würdet ihr das ganz anders aufziehen?

    Samstag, 7. November 2015 21:06
  • Hallo,

    ja, in Dispose wird alles frei gegeben was der GC nicht hin bekommt. Das heißt, dass Klassen ohne IDisposable komplett vom GC entfernt werden. Wenn du selbst nachhelfen musst (besonders bei nativen Dingen wie Streams etc.) musst du das entsprechend selbst in die Disposed-Methode rein packen.

    Zu deiner Klasse: Zunächst mal solltest du die Anfangsbuchstaben von allem öffentlichen groß schreiben.
    Wie du die nun verwendest hängt von deinem Fall ab. Wenn du die Liste nur innerhalb der Methode verwenden willst, dann Variante 1 - ansonsten Variante 2.

    Am besten merkst du dir am besten alle Variablen in einem möglichst kleinen Raum zu deklarieren, das heißt wenn nur eine Methode die Variable braucht, dann deklarierst du sie in der Methode. Wenn dagegen mehrere Elemente die Variable brauchen, dann in der Klasse(/Typ).


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    • Als Antwort markiert PHep Samstag, 7. November 2015 23:33
    Samstag, 7. November 2015 21:25
    Moderator
  • Hi,
    beim Dispose wird der belegte Speicher erst nur zur Freigabe "markiert" und das Objekt kann deshalb nicht mehr benutzt werden. Zur weiteren Verwendung freigegeben wird der ehemals belegte Speicher IMMER durch den Garbage Collector. Mit GC.SuppressFinalize(this); wird dem Garbage Collector mitgeteilt, dass er den Finalizer nicht mehr aufzurufen braucht, bevor er den Speicherplatz endgültig freigibt. Damit wird eine (geringfügige) Beschleunigung der Arbeit des Garbage Collectors erreicht, was bei vielen Objekten ins Gewicht fallen kann. Wenn kein SupressFinalize aufgerufen wird, dann ruft der Garbage Collector vor der Freigabe des Speicherplatzes den Finalizer auf.

    Zur zweiten Frage ist zu bemerken, dass mit List<person>myList = new List<person>(); im Rumpf der Klasse der Speicherplatz für die Liste bereits zum Zeitpunkt der Instanziierung des Objektes belegt wird, d.h. beim myClass newClass = newmyClass();. Wenn nur die Variable für die Liste deklariert wird, wird bei der Instanziierung nur der Speicherplatz für den Verweis auf die Liste im Objekt reserviert (4 oder 8 Byte). Im Methodenaufruf wird dann der Speicherplatz für das Listenobjekt belegt. Damit kann die Belegung des Speicherplatzes verzögert werden. Ein Listenobjekt ohne Elemente belegt aber so wenig Speicherplatz, dass der Zeitpunkt der Instanziierung des Listenobjektes praktisch ohne Bedeutung ist. Vorrangig sollte der Algorithmus sein.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks
    Kommas richtig setzen!
    Schüler sagen, Lehrer haben es gut.
    Schüler, sagen Lehrer, haben es gut

    • Als Antwort markiert PHep Montag, 9. November 2015 22:40
    Sonntag, 8. November 2015 04:48