| 

.NET C# Java Javascript Exception

3
Wahrscheinlich bin ich zu doof, aber wie kann ich in C# ein Objekt wieder freigeben? Hab schon alles versucht.
News:
14.01.2011
littlejon 43 1 3
Anmerkung: Eine gute deutsprachige Erklärung zum Thema Destructor/Finilazer, IDisposable und der Funktionsweise des Garbage-Collectors gibt es unter anderem hier: http://openbook.galileocomputing.de/visual_csharp_2010/visual_csharp_2010_04_008.htm
Floyd 17.01.2011
3 Antworten
3
Durch die Zuweisung von "null" entfernst du die Referenz. Der GC räumt in intervallen oder wenn der Speicher knapp wird alle Objekte weg die nicht mehr Referenziert sind.
Hierzu führt er die Dispose-Methode des IDisposeable-Interfaces aus wenn das Object ein solches Implementiert. Wenn nicht wird der Speicher einfach nur frei gegeben.

Solltest du in deinem Objekt COM- bzw. Unmagedressourcen verwenden musst du das IDisposeable-Interface implementieren damit der GC auch diese freigeben kann.

Grundsätzlich kannst du die Dispose-Methode auch direkt aufrufen um Speicherplatz zu schaffen. In wenigen Fällen ist es auch sinnvoll von der Möglichkeit gebrauch zumachen mittels GC.Collect eine Gargabe Collection zu erzwingen siehe hierzu: http://msdn.microsoft.com/de-de/library/system.gc.collect%28VS.80%29.aspx

Der Ablauf:


Quelle: http://www.codeproject.com/KB/cs/idispose.aspx

Weitere Erklärungen zur Funktionsweise des IDisposeable-Interfaces und des GCs findest du zum Beispiel hier: http://www.codeproject.com/KB/cs/idispose.aspx
14.01.2011
Floyd 14,6k 3 9
Floyd 14,6k 3 9
1
Der Garbage Collector führt Dispose nicht aus, sondern den Finalizer. Dispose muss entweder per Hand oder durch den Finalizer aufgerufen werden - dem GC ist IDisposable egal.

Außerdem erzwingt das reine Setzen *EINER* Referenz auf null noch nicht, dass das Objekt entfernt wird. Erstens nicht, weil nicht gesagt ist, dass die GC überhaupt danach anspringt, zweitens nicht, weil es potenziell nicht die einzige Referenz ist.

Es können sogar Objekte abgeräumt werden, von denen keine einzige Referenz auf null ist. Dann nämlich, wenn alle auf sie verweisenden Objekte nicht mehr referenziert werden.
Golo Roden 14.01.2011
Zu Punkt 2: "Der GC räumt in intervallen oder wenn der Speicher knapp wird alle Objekte weg die nicht mehr Referenziert sind." Ich dachte der Satz würde genau diesen Punkt verdeutlichen. Zu Punkt 3: Das ist zwar im Prinzip richtig aber wenn ich die Referenz des übergeordneten Objects lösche und der GC dann selbiges weg schmeißt, löscht er damit auch die Referenzen auf die untergeordneten Objekte welche wiederrum dann referenzlos und somit löschbar sind. Wobei das implementierungsdetails des GCs sind die für die allgemeine Erklärung als Antwort auf seine Frage mir zu weit gingen.
Floyd 17.01.2011
5
Prinzipiell musst Du das nicht per Hand machen. Alle Objekte, die ausschließlich verwalteten Code betreffen, werden automatisch von der Garbage Collection wieder freigegeben - ein delete-Schlüsselwort wie in C++ gibt es in C# nicht.

Dennoch kann es sinnvoll sein, bestimmte Aufräumarbeiten durchzuführen, wenn ein Objekt nicht mehr benötigt wird. Dazu kannst Du einen Finalizer implementieren, also eine Methode, die quasi wie ein Destruktor aufgerufen wird, bevor das Objekt von der Garbage Collection zerstört wird. Du hast allerdings keinen Einfluss darauf, wann die Garbage Collection dies macht, insofern handelt es sich bei einem Finalizer um irgendwann ablaufenden Code.

Außerdem ist zu beachten, dass ein Finalizer das tatsächliche Entfernen des Objekts aus dem Speicher verzögert (auf Grund der Art, wie die Garbage Collection intern arbeitet). Aus diesem Grund empfiehlt es sich, Finalizer nur dann zu schreiben, wenn es absolut nicht anders möglich ist (sprich, wenn Du auf unverwalteten Code oder unverwaltete Ressourcen zugreifst).

Für ein deterministisches Verfahren gibt es die IDisposable-Schnittstelle, deren einzige Dispose-Methode Du zu einem von Dir gewählten Zeitpunkt aufrufen kannst, um alle gewünschten Aufräumarbeiten zu erledigen. Natürlich kannst Du Dispose auch aus einem Finalizer heraus aufrufen - allerdings musst Du dann aufpassen, dass Du auf keine verwalteten Objekte mehr zugreifst, da diese potenziell schon zerstört sein könnten.

Sobald Dispose gelaufen ist, kann der Finalizer natürlich deaktiviert werden - sonst würde Dispose ja zwei Mal laufen.

Außerdem wird empfohlen, intern ein Flag mitzuführen, ob ein Objekt bereits disposed wurde - falls ja, sollte es keine weiteren Methodenaufrufe mehr annehmen, sondern diese mit einer ObjectDisposedException quittieren.

Ein Beispiel für eine voll ausformulierte Version sähe zB wie folgt aus:

public class Foo : IDisposable
{
private bool _isDisposed;

public void Dispose()
{
this.Dispose(true);
}

public void DoSomething()
{
if(this._isDisposed)
{
throw new ObjectDisposedException();
}

// Do whatever this method should do normally.
}

private void Dispose(isDisposeByUser)
{
if(isDisposeByUser)
{
// Clean managed resources.
// ...
}

// Clean up unmanaged resources.
// ...

// Disable finalizer.
GC.SuppressFinalize(this);

this._isDisposed = true;
}

public ~Foo()
{
this.Dispose(false);
}
}

Interessant ist übrigens noch, dass Du ein Objekt, das IDisposable implementiert, mit using aufrufen kannst. Sobald der using-Block verlassen wird, wird Dispose() automatisch ausgelöst, so dass Du Dich darum nicht mehr per Hand kümmern musst. Dies funktioniert sogar dann, wenn innerhalb des using-Blocks eine Exception geworfen wird.

Beispiel:

using(var foo = new Foo())
{
// ...
} // <== Here, foo.Dispose() is called automatically.

Weitere Informationen dazu findest Du übrigens in der dotnetpro 01.2011 und 02.2011, in meiner Kolumne "Golos scharfes C".
14.01.2011
Golo Roden 2,7k 3 9
1
Deine Implementierung des IDisposeable-Interfaces ist leider falsch. Die Methode Dispose sollte im Fall das "isDisposeByUser" auf true steht nicht unmangedressourcen freigeben sondern managed-ressourcen. private void Dispose(isDisposeByUser)
{
if(isDisposeByUser)
{
// Clean MANAGED resources.
// ...
}

// Clean unmanaged resources.
// ...

// Disable finalizer.
GC.SuppressFinalize(this);

this._isDisposed = true;
}
Floyd 14.01.2011
Mist, vertippt. Ich hab's korrigiert, danke für den Hinweis.
Golo Roden 14.01.2011
Dann gibts jetzt von mit +1 ;)
Floyd 17.01.2011
0
Solange du keine unmanaged Ressourcen verwendest, erledigt das der Garbage Collector für dich.
Du solltest lediglich bei der Verwendung von Klassen die IDisposable implementieren darauf achten, dass du in der verwendenden Klasse ebenfalls IDisposable implementierst und die entsprechenden Objekte mit Dispose freigiebst.

Servus,
Klaus
14.01.2011
klaus_b 1,6k 3 7

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH