Ich weiß aus der Lektüre [der Microsoft-Dokumentation] (https://docs.microsoft.com/dotnet/api/system.idisposable), dass die "primäre" Verwendung der "IDisposable"-Schnittstelle darin besteht, nicht verwaltete Ressourcen aufzuräumen.
Für mich bedeutet "nicht verwaltet" Dinge wie Datenbankverbindungen, Sockets, Fensterhandles, etc. Aber ich habe Code gesehen, in dem die Methode Dispose()
implementiert ist, um verwaltete Ressourcen freizugeben, was mir überflüssig erscheint, da der Garbage Collector dies für Sie erledigen sollte.
Zum Beispiel:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Meine Frage ist, ob der Garbage Collector dadurch den von "MyCollection" verwendeten Speicher schneller freigibt, als er es normalerweise tun würde?
edit: Bisher haben die Leute einige gute Beispiele für die Verwendung von IDisposable zum Aufräumen von nicht verwalteten Ressourcen wie Datenbankverbindungen und Bitmaps gepostet. Aber nehmen wir an, _theList
im obigen Code enthielte eine Million Strings, und Sie wollten diesen Speicher jetzt freigeben, anstatt auf den Garbage Collector zu warten. Würde der obige Code das bewerkstelligen?
IDisposable
wird oft verwendet, um die using
-Anweisung auszunutzen und einen einfachen Weg zur deterministischen Bereinigung von verwalteten Objekten zu nutzen.
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
Ja, dieser Code ist völlig überflüssig und unnötig, und er lässt den Garbage Collector nichts tun, was er nicht auch sonst tun würde (sobald eine Instanz von MyCollection den Gültigkeitsbereich verlässt), insbesondere die Clear()-Aufrufe.
Antwort auf Ihre Bearbeitung: Irgendwie schon. Wenn ich dies tue:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Es ist funktionell identisch mit dem hier, was die Speicherverwaltung angeht:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Wenn Sie den Speicher wirklich sofort freigeben muessen, rufen Sie GC.Collect()
auf. Es gibt jedoch keinen Grund, dies hier zu tun. Der Speicher wird freigegeben, wenn er gebraucht wird.
Wenn MyCollection
ohnehin in den Garbage Collector geht, sollten Sie sie nicht entsorgen müssen. Dies würde die CPU nur mehr als nötig belasten und könnte sogar einige vorberechnete Analysen, die der Garbage Collector bereits durchgeführt hat, ungültig machen.
Ich verwende IDisposable
, um sicherzustellen, dass Threads korrekt entsorgt werden, zusammen mit nicht verwalteten Ressourcen.
EDIT Als Antwort auf Scotts Kommentar:
_Die einzige Zeit, in der die GC-Leistungsmetriken betroffen sind, ist, wenn ein Aufruf der [sic] GC.Collect() gemacht wird".
Konzeptionell verwaltet die GC eine Ansicht des Objektreferenzgraphen und alle Verweise darauf aus den Stapelrahmen der Threads. Dieser Heap kann recht groß sein und sich über viele Seiten des Speichers erstrecken. Aus Optimierungsgründen legt der GC seine Analyse von Seiten, die sich wahrscheinlich nicht oft ändern werden, in einem Cache ab, um ein unnötiges erneutes Scannen der Seite zu vermeiden. Der GC wird vom Kernel benachrichtigt, wenn sich Daten auf einer Seite ändern, so dass er weiß, dass die Seite verschmutzt ist und neu gescannt werden muss. Wenn sich die Sammlung in Gen0 befindet, ist es wahrscheinlich, dass sich auch andere Dinge auf der Seite ändern, aber in Gen1 und Gen2 ist dies weniger wahrscheinlich. Dem Team, das die GC auf den Mac portiert hat, um das Silverlight-Plug-in auf dieser Plattform zum Laufen zu bringen, standen diese Hooks unter Mac OS X anekdotischerweise nicht zur Verfügung.
Ein weiterer Punkt gegen unnötige Ressourcenvergeudung: Stellen Sie sich eine Situation vor, in der ein Prozess entladen wird. Stellen Sie sich außerdem vor, dass der Prozess bereits seit einiger Zeit läuft. Wahrscheinlich sind viele der Speicherseiten dieses Prozesses auf die Festplatte ausgelagert worden. Zumindest befinden sie sich nicht mehr im L1- oder L2-Cache. In einer solchen Situation macht es für eine Anwendung, die entladen wird, keinen Sinn, all diese Daten- und Codeseiten wieder in den Speicher auszulagern, um Ressourcen "freizugeben", die beim Beenden des Prozesses ohnehin vom Betriebssystem freigegeben werden. Dies gilt für verwaltete und sogar für bestimmte nicht verwaltete Ressourcen. Nur Ressourcen, die Nicht-Hintergrund-Threads am Leben erhalten, müssen entsorgt werden, sonst bleibt der Prozess am Leben.
Nun gibt es während der normalen Ausführung ephemere Ressourcen, die korrekt aufgeräumt werden müssen (wie @fezmonkey anmerkt Datenbankverbindungen, Sockets, Fenster-Handles), um nicht verwaltete Speicherlecks zu vermeiden. Dies sind die Arten von Dingen, die entsorgt werden müssen. Wenn Sie eine Klasse erstellen, die einen Thread besitzt (und mit "besitzt" meine ich, dass sie ihn erstellt hat und daher dafür verantwortlich ist, dass er anhält, zumindest nach meinem Kodierungsstil), dann muss diese Klasse höchstwahrscheinlich IDisposable
implementieren und den Thread während Dispose
abbauen.
Das .NET-Framework verwendet die IDisposable'-Schnittstelle als Signal, ja sogar als Warnung für Entwickler, dass diese Klasse entsorgt werden _muss_. Mir fallen keine Typen im Framework ein, die
IDisposable` implementieren (außer expliziten Schnittstellenimplementierungen), bei denen die Entsorgung optional ist.