Ich weiß aus der Microsoft-Dokumentation, dass die "primäre" Verwendung der IDisposable
Schnittstelle darin besteht, nicht verwaltete Ressourcen zu bereinigen.
Für mich bedeutet "nicht verwaltet" Dinge wie Datenbankverbindungen, Sockets, Fensterhandles usw. Aber ich habe Code gesehen, in dem die Dispose()
Methode implementiert ist, um verwaltete Ressourcen freizugeben , was mir überflüssig erscheint, da sich der Garbage Collector darum kümmern sollte Dies für dich.
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, macht dies den freien Speicher des Garbage Collectors MyCollection
schneller als normalerweise?
Bearbeiten : Bisher haben einige Leute einige gute Beispiele für die Verwendung von IDisposable veröffentlicht, um nicht verwaltete Ressourcen wie Datenbankverbindungen und Bitmaps zu bereinigen. Angenommen, _theList
der obige Code enthielt eine Million Zeichenfolgen, und Sie wollten diesen Speicher jetzt freigeben , anstatt auf den Garbage Collector zu warten. Würde der obige Code das erreichen?
IDisposable
markiert nichts. DieDispose
Methode unternimmt das, was sie tun muss, um die von der Instanz verwendeten Ressourcen zu bereinigen. Dies hat nichts mit GC zu tun.IDisposable
. Und aus diesem Grund habe ich gesagt, dass die akzeptierte Antwort nicht die beabsichtigte Frage (und die nachfolgende Bearbeitung) des OP beantwortet, ob IDisposable bei der <i> Freigabe von Speicher </ i> hilfreich ist. Da diesIDisposable
nichts mit der Freigabe von Speicher zu tun hat, sondern nur mit Ressourcen. Wie Sie bereits sagten, müssen die verwalteten Verweise überhaupt nicht auf null gesetzt werden, was OP in seinem Beispiel getan hat. Die richtige Antwort auf seine Frage lautet also: "Nein, es hilft nicht, Speicher schneller freizugeben. Tatsächlich hilft es überhaupt nicht, Speicher freizugeben, nur Ressourcen." Trotzdem danke für deine Eingabe.Antworten:
Der Punkt der Entsorgung besteht darin , nicht verwaltete Ressourcen freizugeben. Es muss irgendwann gemacht werden, sonst werden sie nie aufgeräumt. Der Garbage Collector weiß nicht, wie er
DeleteHandle()
eine Variable vom Typ aufruftIntPtr
, er weiß nicht, ob er aufrufen muss oder nichtDeleteHandle()
.Das von Ihnen erstellte Objekt muss eine Methode verfügbar machen , die die Außenwelt aufrufen kann, um nicht verwaltete Ressourcen zu bereinigen. Die Methode kann beliebig benannt werden:
oder
Stattdessen gibt es einen standardisierten Namen für diese Methode:
Es wurde sogar eine Schnittstelle erstellt
IDisposable
, die nur diese eine Methode hat:So machen Sie Ihr Objekt die
IDisposable
Schnittstelle und versprechen auf diese Weise, dass Sie diese einzige Methode geschrieben haben, um Ihre nicht verwalteten Ressourcen zu bereinigen:Und du bist fertig. Außer du kannst es besser machen.
Was ist, wenn Ihr Objekt eine 250 MB System.Drawing.Bitmap (dh die von .NET verwaltete Bitmap-Klasse) als eine Art Frame-Puffer zugewiesen hat ? Sicher, dies ist ein verwaltetes .NET-Objekt, und der Garbage Collector gibt es frei. Aber möchten Sie wirklich 250 MB Speicher nur dort sitzen lassen und darauf warten, dass der Garbage Collector es tut? irgendwann kommt und ihn ? Was ist, wenn eine offene Datenbankverbindung besteht ? Sicherlich wollen wir nicht, dass diese Verbindung offen bleibt und darauf wartet, dass der GC das Objekt fertigstellt.
Wenn der Benutzer angerufen hat
Dispose()
(was bedeutet, dass er das Objekt nicht mehr verwenden möchte), warum nicht diese verschwenderischen Bitmaps und Datenbankverbindungen loswerden?Also jetzt werden wir:
Aktualisieren wir also unsere
Dispose()
Methode, um diese verwalteten Objekte zu entfernen:Und alles ist gut, außer du kannst es besser machen !
Was ist, wenn die Person vergessen hat ,
Dispose()
Ihr Objekt anzurufen ? Dann würden sie einige nicht verwaltete Ressourcen verlieren !Wenn die Person vergessen hat anzurufen
Dispose()
, können wir trotzdem ihren Speck retten! Wir haben immer noch eine Möglichkeit, es für sie zu nennen : Wenn der Garbage Collector endlich dazu kommt, unser Objekt freizugeben (dh abzuschließen).Die Zerstörung unseres Objekts durch den Garbage Collector ist der perfekte Zeitpunkt, um diese lästigen, nicht verwalteten Ressourcen freizugeben. Wir tun dies, indem wir die
Finalize()
Methode überschreiben .Aber dieser Code enthält einen Fehler. Sie sehen, der Garbage Collector läuft auf einem Hintergrund-Thread . Sie kennen nicht die Reihenfolge, in der zwei Objekte zerstört werden. Es ist durchaus möglich, dass in Ihrem
Dispose()
Code das verwaltete Objekt, das Sie entfernen möchten (weil Sie hilfreich sein wollten), nicht mehr vorhanden ist:Sie müssen also
Finalize()
feststellen,Dispose()
dass keine verwalteten Ressourcen berührt werden dürfen (da diese möglicherweise nicht vorhanden sind) sind), während freigegeben werden.Das Standardmuster , dies zu tun zu haben ist
Finalize()
undDispose()
beide Anruf eine dritte Methode (!); Wenn Sie ein boolesches Sprichwort übergeben, von dem aus Sie es aufrufenDispose()
(im Gegensatz zuFinalize()
), bedeutet dies, dass verwaltete Ressourcen sicher freigegeben werden können.Diese interne Methode könnte einen beliebigen Namen wie "CoreDispose" oder "MyInternalDispose" erhalten, wird aber traditionell so genannt
Dispose(Boolean)
:Ein hilfreicherer Parametername könnte jedoch sein:
Und Sie ändern Ihre Implementierung der
IDisposable.Dispose()
Methode in:und Ihr Finalizer für:
Und alles ist gut, außer du kannst es besser machen !
Wenn der Benutzer
Dispose()
Ihr Objekt aufruft , wurde alles bereinigt. Später, wenn der Garbage Collector vorbeikommt und Finalize aufruft, wird erDispose
erneut aufgerufen .Dies ist nicht nur verschwenderisch, sondern wenn Ihr Objekt Junk-Verweise auf Objekte enthält, die Sie bereits beim letzten Aufruf entsorgt haben
Dispose()
, werden Sie versuchen, diese erneut zu entsorgen!Sie werden feststellen, dass ich in meinem Code darauf geachtet habe, Verweise auf Objekte zu entfernen, die ich entsorgt habe, damit ich nicht versuche, sie aufzurufen
Dispose
eine Junk-Objektreferenz . Aber das hinderte einen subtilen Käfer nicht daran, sich einzuschleichen.Wenn der Benutzer Folgendes aufruft
Dispose()
: Das Handle CursorFileBitmapIconServiceHandle wird zerstört. Später, wenn der Garbage Collector ausgeführt wird, versucht er, denselben Handle erneut zu zerstören.Wenn Sie dies beheben, teilen Sie dem Garbage Collector mit, dass er sich nicht um die Fertigstellung des Objekts kümmern muss. Die Ressourcen wurden bereits bereinigt, und es ist keine weitere Arbeit erforderlich. Sie tun dies, indem Sie
GC.SuppressFinalize()
dieDispose()
Methode aufrufen :Nachdem der Benutzer angerufen hat
Dispose()
, haben wir:Es macht keinen Sinn, dass der GC den Finalizer ausführt - alles ist erledigt.
Könnte ich Finalize nicht verwenden, um nicht verwaltete Ressourcen zu bereinigen?
Die Dokumentation für
Object.Finalize
sagt:In der MSDN-Dokumentation heißt es jedoch auch
IDisposable.Dispose
:Also was ist es? An welchem Ort kann ich nicht verwaltete Ressourcen bereinigen? Die Antwort ist:
Sie könnten Ihre nicht verwaltete Bereinigung sicherlich in den Finalizer stellen:
Das Problem dabei ist, dass Sie keine Ahnung haben, wann der Garbage Collector Ihr Objekt fertigstellen wird. Ihre nicht verwalteten, nicht benötigten und nicht verwendeten nativen Ressourcen bleiben erhalten, bis der Garbage Collector schließlich ausgeführt wird . Dann wird Ihre Finalizer-Methode aufgerufen. Bereinigen nicht verwalteter Ressourcen. Die Dokumentation von Object.Finalize weist darauf hin:
Dies ist die Tugend der
Dispose
Bereinigung nicht verwalteter Ressourcen. Sie erfahren und steuern, wann nicht verwaltete Ressourcen bereinigt werden. Ihre Zerstörung ist "deterministisch" .Um Ihre ursprüngliche Frage zu beantworten: Warum nicht jetzt Speicher freigeben, anstatt wenn der GC sich dazu entscheidet? Ich habe eine Gesichtserkennungs - Software , dass Bedürfnisse von 530 MB interner Bilder loszuwerden jetzt , da sie nicht mehr benötigt werden . Wenn wir es nicht tun: Die Maschine kommt zum Stillstand.
Bonuslesung
Für alle, die den Stil dieser Antwort mögen (das Warum erklären , damit das Wie offensichtlich wird), empfehle ich Ihnen, Kapitel 1 von Don Box's Essential COM zu lesen:
Auf 35 Seiten erklärt er die Probleme bei der Verwendung von Binärobjekten und erfindet COM vor Ihren Augen. Sobald Sie das Warum von COM erkannt haben, sind die verbleibenden 300 Seiten offensichtlich und beschreiben lediglich die Implementierung von Microsoft.
Ich denke, jeder Programmierer, der sich jemals mit Objekten oder COM befasst hat, sollte zumindest das erste Kapitel lesen. Es ist die beste Erklärung für alles, was es je gab.
Extra Bonus Lesung
Wenn alles, was Sie wissen, von Eric Lippert falsch ist
quelle
null
. Erstens bedeutet dies, dass Sie sie nicht erstellen könnenreadonly
, und zweitens müssen Sie sehr hässliche!=null
Überprüfungen durchführen (wie im Beispielcode). Sie könnten eine Flagge habendisposed
, aber es ist einfacher, sich nicht darum zu kümmern. Der .NET GC ist aggressiv genug, dass ein Verweis auf ein Feldx
nicht mehr als "verwendet" gezählt wird, wenn er diex.Dispose()
Linie passiert .IDisposable
wird häufig verwendet, um dieusing
Anweisung auszunutzen und eine einfache Möglichkeit zur deterministischen Bereinigung verwalteter Objekte zu nutzen.quelle
Der Zweck des Dispose-Musters besteht darin, einen Mechanismus zum Bereinigen sowohl verwalteter als auch nicht verwalteter Ressourcen bereitzustellen. Wann dies auftritt, hängt davon ab, wie die Dispose-Methode aufgerufen wird. In Ihrem Beispiel bewirkt die Verwendung von Dispose eigentlich nichts im Zusammenhang mit Dispose, da das Löschen einer Liste keine Auswirkungen auf die Entsorgung dieser Sammlung hat. Ebenso haben die Aufrufe zum Setzen der Variablen auf Null keine Auswirkungen auf den GC.
In diesem Artikel finden Sie weitere Informationen zum Implementieren des Dispose-Musters. Im Grunde sieht es jedoch so aus:
Die Methode, die hier am wichtigsten ist, ist die Dispose (bool), die tatsächlich unter zwei verschiedenen Umständen ausgeführt wird:
Das Problem, wenn Sie den GC einfach für die Bereinigung sorgen lassen, besteht darin, dass Sie keine wirkliche Kontrolle darüber haben, wann der GC einen Erfassungszyklus ausführt (Sie können GC.Collect () aufrufen, sollten dies aber wirklich nicht tun), damit die Ressourcen möglicherweise erhalten bleiben länger als nötig. Denken Sie daran, dass der Aufruf von Dispose () keinen Erfassungszyklus verursacht oder in irgendeiner Weise dazu führt, dass der GC das Objekt sammelt / freigibt. Es bietet lediglich die Möglichkeit, die verwendeten Ressourcen deterministischer zu bereinigen und dem GC mitzuteilen, dass diese Bereinigung bereits durchgeführt wurde.
Der springende Punkt bei IDisposable und dem Entsorgungsmuster besteht nicht darin, sofort Speicher freizugeben. Das einzige Mal, dass ein Aufruf von Dispose tatsächlich die Chance hat, sofort Speicher freizugeben, ist, wenn das Szenario disposing == false behandelt und nicht verwaltete Ressourcen manipuliert werden. Bei verwaltetem Code wird der Speicher erst dann wiederhergestellt, wenn der GC einen Erfassungszyklus ausführt, über den Sie wirklich keine Kontrolle haben (außer das Aufrufen von GC.Collect (), das ich bereits erwähnt habe, ist keine gute Idee).
Ihr Szenario ist nicht wirklich gültig, da Zeichenfolgen in .NET keine unveränderten Ressourcen verwenden und IDisposable nicht implementieren. Es gibt keine Möglichkeit, die "Bereinigung" zu erzwingen.
quelle
Nach dem Aufruf von Dispose sollten die Methoden eines Objekts nicht mehr aufgerufen werden (obwohl ein Objekt weitere Aufrufe von Dispose tolerieren sollte). Daher ist das Beispiel in der Frage albern. Wenn Dispose aufgerufen wird, kann das Objekt selbst verworfen werden. Daher sollte der Benutzer einfach alle Verweise auf das gesamte Objekt verwerfen (auf null setzen), und alle darin enthaltenen verwandten Objekte werden automatisch bereinigt.
Was die allgemeine Frage zu verwaltet / nicht verwaltet und die Diskussion in anderen Antworten betrifft, muss jede Antwort auf diese Frage mit einer Definition einer nicht verwalteten Ressource beginnen.
Es läuft darauf hinaus, dass es eine Funktion gibt, die Sie aufrufen können, um das System in einen Zustand zu versetzen, und eine andere Funktion, die Sie aufrufen können, um es wieder aus diesem Zustand zu bringen. Im typischen Beispiel könnte die erste eine Funktion sein, die ein Dateihandle zurückgibt, und die zweite könnte ein Aufruf von sein
CloseHandle
.Aber - und das ist der Schlüssel - sie können jedes passende Funktionspaar sein. Einer baut einen Zustand auf, der andere reißt ihn ab. Wenn der Status erstellt, aber noch nicht abgerissen wurde, ist eine Instanz der Ressource vorhanden. Sie müssen dafür sorgen, dass der Abbau zum richtigen Zeitpunkt erfolgt - die Ressource wird nicht von der CLR verwaltet. Der einzige automatisch verwaltete Ressourcentyp ist der Speicher. Es gibt zwei Arten: den GC und den Stapel. Werttypen werden vom Stapel verwaltet (oder durch Anhängen einer Fahrt innerhalb von Referenztypen), und Referenztypen werden vom GC verwaltet.
Diese Funktionen können Statusänderungen verursachen, die frei verschachtelt werden können oder perfekt verschachtelt sein müssen. Die Statusänderungen sind möglicherweise threadsicher oder nicht.
Schauen Sie sich das Beispiel in der Frage der Justiz an. Änderungen am Einzug der Protokolldatei müssen perfekt verschachtelt sein, sonst geht alles schief. Es ist auch unwahrscheinlich, dass sie threadsicher sind.
Es ist möglich, eine Fahrt mit dem Müllsammler zu unternehmen, um Ihre nicht verwalteten Ressourcen zu bereinigen. Aber nur wenn die Zustandsänderungsfunktionen threadsicher sind und zwei Zustände Lebensdauern haben können, die sich in irgendeiner Weise überlappen. Das Beispiel einer Ressource für Gerechtigkeit darf also KEINEN Finalisierer haben! Es würde einfach niemandem helfen.
Für diese Art von Ressourcen können Sie sie einfach
IDisposable
ohne Finalizer implementieren . Der Finalizer ist absolut optional - es muss sein. Dies wird in vielen Büchern beschönigt oder gar nicht erwähnt.Sie müssen dann die
using
Anweisung verwenden, um sicherzustellen, dass sieDispose
aufgerufen wird. Dies ist im Wesentlichen so, als würde man eine Fahrt mit dem Stapel ankuppeln (so wie der Finalizer für den GC ist,using
ist er für den Stapel).Der fehlende Teil ist, dass Sie Dispose manuell schreiben und es auf Ihre Felder und Ihre Basisklasse aufrufen müssen. C ++ / CLI-Programmierer müssen das nicht tun. Der Compiler schreibt es in den meisten Fällen für sie.
Es gibt eine Alternative, die ich für Zustände bevorzuge, die perfekt verschachtelt und nicht threadsicher sind (abgesehen von allem anderen erspart Ihnen das Vermeiden von IDisposable das Problem, mit jemandem zu streiten, der nicht widerstehen kann, jeder Klasse, die IDisposable implementiert, einen Finalizer hinzuzufügen). .
Anstatt eine Klasse zu schreiben, schreiben Sie eine Funktion. Die Funktion akzeptiert einen Delegaten, an den zurückgerufen werden kann:
Und dann wäre ein einfaches Beispiel:
Das übergebene Lambda dient als Codeblock. Sie erstellen also Ihre eigene Kontrollstruktur, um denselben Zweck zu erfüllen
using
, außer dass Sie keine Gefahr mehr haben, dass der Anrufer es missbraucht. Es gibt keine Möglichkeit, die Ressource zu bereinigen.Diese Technik ist weniger nützlich, wenn die Ressource überlappende Lebensdauern aufweist, da Sie dann Ressource A, dann Ressource B, dann Ressource A und später Ressource B erstellen möchten. Das können Sie nicht wenn Sie den Benutzer gezwungen haben, so perfekt zu verschachteln. Aber dann müssen Sie verwenden
IDisposable
(aber immer noch ohne Finalizer, es sei denn, Sie haben Threadsafety implementiert, die nicht kostenlos ist).quelle
enter
undexit
ist der Kern meiner Meinung nach einer Ressource. Das Abonnieren / Abbestellen von Veranstaltungen sollte problemlos dazu passen. In Bezug auf die orthogonalen / fungiblen Eigenschaften ist es praktisch nicht von einem Speicherverlust zu unterscheiden. (Dies ist nicht überraschend, da Abonnement nur Objekte zu einer Liste hinzufügt.)Szenarien, die ich mit IDisposable verwende: Nicht verwaltete Ressourcen bereinigen, Ereignisse abbestellen, Verbindungen schließen
Die Redewendung, die ich für die Implementierung von IDisposable verwende ( nicht threadsicher ):
quelle
Ja, dieser Code ist völlig redundant und unnötig und lässt den Garbage Collector nichts tun, was er sonst nicht tun würde (sobald eine Instanz von MyCollection den Gültigkeitsbereich verlässt). Insbesondere die
.Clear()
Aufrufe.Antwort auf Ihre Bearbeitung: Art von. Wenn ich das mache:
Für die Speicherverwaltung ist es funktional identisch damit:
Wenn Sie den Speicher wirklich sofort freigeben müssen, rufen Sie an
GC.Collect()
. Hier gibt es jedoch keinen Grund, dies zu tun. Der Speicher wird freigegeben, wenn er benötigt wird.quelle
Wenn
MyCollection
sowieso Müll gesammelt wird, sollten Sie ihn nicht entsorgen müssen. Wenn Sie dies tun, wird die CPU nur mehr als nötig aufgewühlt, und möglicherweise wird sogar eine vorberechnete Analyse ungültig, die der Garbage Collector bereits durchgeführt hat.Ich
IDisposable
mache Dinge wie sicherzustellen, dass Threads korrekt entsorgt werden, zusammen mit nicht verwalteten Ressourcen.BEARBEITEN Als Antwort auf Scotts Kommentar:
Konzeptionell behält der GC eine Ansicht des Objektreferenzdiagramms und aller Verweise darauf aus den Stapelrahmen von Threads bei. Dieser Heap kann sehr groß sein und viele Speicherseiten umfassen. Als Optimierung speichert der GC die Analyse von Seiten zwischen, deren Änderung wahrscheinlich nicht sehr häufig erfolgt, um ein unnötiges erneutes Scannen der Seite zu vermeiden. Der GC erhält eine Benachrichtigung vom Kernel, wenn sich Daten auf einer Seite ändern, sodass er weiß, dass die Seite verschmutzt ist und einen erneuten Scan erfordert. Wenn sich die Sammlung in Gen0 befindet, ändern sich wahrscheinlich auch andere Dinge auf der Seite, dies ist jedoch in Gen1 und Gen2 weniger wahrscheinlich. Anekdotisch waren diese Hooks in Mac OS X für das Team, das den GC auf Mac portierte, nicht verfügbar, damit das Silverlight-Plug-In auf dieser Plattform funktioniert.
Ein weiterer Punkt gegen unnötige Entsorgung von Ressourcen: Stellen Sie sich eine Situation vor, in der ein Prozess entladen wird. Stellen Sie sich auch vor, dass der Prozess seit einiger Zeit ausgeführt wird. Es besteht die Möglichkeit, dass viele der Speicherseiten dieses Prozesses auf die Festplatte ausgelagert wurden. Zumindest befinden sie sich nicht mehr im L1- oder L2-Cache. In einer solchen Situation macht es keinen Sinn, dass eine entladene Anwendung all diese Daten und Codepages zurück in den Speicher tauscht, um Ressourcen freizugeben, die vom Betriebssystem ohnehin freigegeben werden, wenn der Prozess beendet wird. Dies gilt für verwaltete und sogar bestimmte nicht verwaltete Ressourcen. Es müssen nur Ressourcen entsorgt werden, die Nicht-Hintergrund-Threads am Leben erhalten. Andernfalls bleibt der Prozess am Leben.
Jetzt gibt es während der normalen Ausführung kurzlebige Ressourcen, die korrekt bereinigt werden müssen (wie @fezmonkey auf Datenbankverbindungen, Sockets und Fensterhandles hinweist ), um nicht verwaltete Speicherlecks zu vermeiden. Dies sind die Dinge, die entsorgt werden müssen. Wenn Sie eine Klasse erstellen, die einen Thread besitzt (und mit eigenen meine ich, dass sie ihn erstellt hat und daher dafür verantwortlich ist, dass er zumindest durch meinen Codierungsstil stoppt), muss diese Klasse
IDisposable
den Thread höchstwahrscheinlich während des Threads implementieren und abreißenDispose
.Das .NET Framework verwendet die
IDisposable
Schnittstelle als Signal und sogar als Warnung an Entwickler, dass diese Klasse entsorgt werden muss . Ich kann mir keine Typen im Framework vorstellen, die implementierenIDisposable
(ausgenommen explizite Schnittstellenimplementierungen), bei denen die Entsorgung optional ist.quelle
Dispose()
Aufrufe siehe: stackoverflow.com/questions/913228/…In dem von Ihnen geposteten Beispiel wird der Speicher immer noch nicht freigegeben. Der gesamte Speicher wird durch Müll gesammelt, es kann jedoch möglich sein, dass der Speicher in einer früheren Generation gesammelt wird . Sie müssten einige Tests durchführen, um sicherzugehen.
Die Framework Design Guidelines sind Richtlinien und keine Regeln. Sie sagen Ihnen, wofür die Benutzeroberfläche in erster Linie gedacht ist, wann sie verwendet werden soll, wie sie verwendet werden soll und wann sie nicht verwendet werden soll.
Ich habe einmal Code gelesen, der ein einfaches RollBack () bei einem Fehler mit IDisposable war. Die unten stehende MiniTx-Klasse prüft ein Flag auf Dispose (), und wenn der
Commit
Aufruf nie erfolgt ist, ruftRollback
er sich selbst auf. Es wurde eine Indirektionsebene hinzugefügt, die das Aufrufen und Verwalten des aufrufenden Codes erheblich erleichtert. Das Ergebnis sah ungefähr so aus:Ich habe auch gesehen, dass Timing- / Protokollierungscode dasselbe tun. In diesem Fall stoppte die Dispose () -Methode den Timer und protokollierte, dass der Block beendet wurde.
Hier sind einige konkrete Beispiele, die keine nicht verwaltete Ressourcenbereinigung durchführen, aber IDisposable erfolgreich zum Erstellen von sauberem Code verwenden.
quelle
Wenn Sie jetzt löschen möchten , verwenden Sie nicht verwalteten Speicher .
Sehen:
quelle
Ich werde die üblichen Dinge über das Verwenden oder Freigeben nicht verwalteter Ressourcen nicht wiederholen, die alle behandelt wurden. Ich möchte jedoch darauf hinweisen, was ein weit verbreitetes Missverständnis zu sein scheint.
Gegeben den folgenden Code
Mir ist klar, dass die Disposable-Implementierung nicht den aktuellen Richtlinien entspricht, aber hoffentlich haben Sie alle die Idee.
Wenn Dispose aufgerufen wird, wie viel Speicher wird freigegeben?
Antwort: Keine. Es macht keinen Sinn, einen Finaliser hinzuzufügen, um die oben gezeigte Dispose-Methode aufzurufen. Dies verzögert lediglich die erneute Inanspruchnahme des Speichers, damit der Finalisierer ausgeführt werden kann.
Durch Aufrufen von Dispose können nicht verwaltete Ressourcen freigegeben werden. Der verwaltete Speicher kann NICHT zurückgefordert werden. Dies kann nur der GC. Das heißt nicht, dass das oben Genannte keine gute Idee ist. Das Befolgen des obigen Musters ist in der Tat immer noch eine gute Idee. Sobald Dispose ausgeführt wurde, kann der GC den von _Large verwendeten Speicher nicht mehr beanspruchen, obwohl die Instanz von LargeStuff möglicherweise noch im Gültigkeitsbereich ist. Die Zeichenfolgen in _Large befinden sich möglicherweise auch in Gen 0, aber die Instanz von LargeStuff ist möglicherweise in Gen 2, sodass der Speicher wieder früher beansprucht wird.
quelle
LargeStuff
lange genug existiert hat, um es bis zur 2. Generation zu schaffen, und wenn sie_Large
einen Verweis auf eine neu erstellte Zeichenfolge enthält, die sich in der Generation 0 befindet, wird die Zeichenfolge von verwiesen , wenn die Instanz vonLargeStuff
ohne Nullung abgebrochen_Large
wird_Large
wird bis zur nächsten Gen2-Kollektion aufbewahrt. Durch Nullstellen wird_Large
der String möglicherweise bei der nächsten Gen0-Sammlung entfernt. In den meisten Fällen ist das Aufheben von Referenzen nicht hilfreich, aber es gibt Fälle, in denen dies einen gewissen Nutzen bringen kann.Abgesehen von seiner primären Verwendung zur Steuerung der Lebensdauer von Systemressourcen (vollständig abgedeckt durch die großartige Antwort von Ian , Kudos!) Kann die Kombination IDisposable / using auch verwendet werden , um die Statusänderung von (kritischen) globalen Ressourcen zu erfassen : die Konsole , die Threads , der Prozess , jedes globale Objekt wie eine Anwendungsinstanz .
Ich habe einen Artikel über dieses Muster geschrieben: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Es zeigt, wie Sie einen häufig verwendeten globalen Status wiederverwendbar und lesbar schützen können : Konsolenfarben , aktuelle Thread-Kultur , Eigenschaften von Excel-Anwendungsobjekten ...
quelle
Wenn überhaupt, würde ich erwarten, dass der Code weniger effizient ist als beim Auslassen.
Das Aufrufen der Clear () -Methoden ist nicht erforderlich, und der GC würde dies wahrscheinlich nicht tun, wenn Dispose dies nicht tun würde ...
quelle
Es gibt Dinge, die die
Dispose()
Operation im Beispielcode ausführt, die möglicherweise einen Effekt haben, der aufgrund eines normalen GC des nicht auftreten würdeMyCollection
Objekts .Wenn die referenzierten Objekte
_theList
oder_theDict
durch andere Objekte bezeichnet werden, dann ist dasList<>
oderDictionary<>
Objekt nicht Gegenstand Sammlung sein , sondern plötzlich keinen Inhalt haben. Wenn es keine Dispose () -Operation wie im Beispiel gäbe, würden diese Sammlungen immer noch ihren Inhalt enthalten.Wenn dies die Situation wäre, würde ich es natürlich als defektes Design bezeichnen - ich weise nur (pedantisch, nehme ich an) darauf hin, dass der
Dispose()
Vorgang möglicherweise nicht vollständig redundant ist, je nachdem, ob es andere Verwendungszwecke gibtList<>
oderDictionary<>
nicht im Fragment gezeigt.quelle
Ein Problem bei den meisten Diskussionen über "nicht verwaltete Ressourcen" besteht darin, dass sie den Begriff nicht wirklich definieren, aber zu implizieren scheinen, dass er etwas mit nicht verwaltetem Code zu tun hat. Zwar sind viele Arten von nicht verwalteten Ressourcen mit nicht verwaltetem Code verbunden, doch ist es nicht hilfreich, an nicht verwaltete Ressourcen in solchen Begriffen zu denken.
Stattdessen sollte man erkennen, was alle verwalteten Ressourcen gemeinsam haben: Sie alle beinhalten ein Objekt, das ein externes „Ding“ auffordert, etwas in seinem Namen zu tun, zum Nachteil einiger anderer „Dinge“, und die andere Entität stimmt zu, dies bis zu tun auf weiteres. Wenn das Objekt verlassen würde und spurlos verschwinden würde, würde nichts dem äußeren „Ding“ jemals sagen, dass es sein Verhalten für das nicht mehr existierende Objekt nicht mehr ändern muss; folglich würde die Nützlichkeit des Dings dauerhaft verringert.
Eine nicht verwaltete Ressource stellt also eine Vereinbarung eines externen "Dings" dar, sein Verhalten im Namen eines Objekts zu ändern, was die Nützlichkeit dieses externen "Dings" nutzlos beeinträchtigen würde, wenn das Objekt verlassen würde und nicht mehr existiert. Eine verwaltete Ressource ist ein Objekt, das von einer solchen Vereinbarung profitiert, sich jedoch für den Erhalt einer Benachrichtigung angemeldet hat, wenn sie aufgegeben wird, und das diese Benachrichtigung verwendet, um seine Angelegenheiten in Ordnung zu bringen, bevor sie zerstört wird.
quelle
IDisposable
ist gut zum Abbestellen von Veranstaltungen.quelle
Erste Definition. Für mich bedeutet nicht verwaltete Ressource eine Klasse, die eine IDisposable-Schnittstelle oder etwas implementiert, das unter Verwendung von Aufrufen der DLL erstellt wurde. GC weiß nicht, wie er mit solchen Objekten umgehen soll. Wenn die Klasse zum Beispiel nur Werttypen hat, betrachte ich diese Klasse nicht als Klasse mit nicht verwalteten Ressourcen. Für meinen Code folge ich den nächsten Übungen:
folgende Vorlage zeigt, was ich in Worten als Codebeispiel beschrieben habe:
quelle
is IDisposable
selbst als nicht verwaltete Ressource betrachtet werden sollte? Das scheint nicht richtig zu sein. Auch wenn der Implementierungstyp ein reiner Werttyp ist, scheinen Sie darauf hinzuweisen, dass er nicht entsorgt werden muss. Das scheint auch falsch.Ihr angegebenes Codebeispiel ist kein gutes Beispiel für die
IDisposable
Verwendung. Das Löschen von Wörterbüchern sollte normalerweise nicht zurDispose
Methode gehen. Wörterbuchelemente werden gelöscht und entsorgt, wenn der Gültigkeitsbereich überschritten wird.IDisposable
Die Implementierung ist erforderlich, um einige Speicher / Handler freizugeben, die auch dann nicht freigegeben / freigegeben werden, wenn sie außerhalb des Gültigkeitsbereichs liegen.Das folgende Beispiel zeigt ein gutes Beispiel für ein IDisposable-Muster mit Code und Kommentaren.
quelle
Der gerechtfertigtste Anwendungsfall für die Entsorgung verwalteter Ressourcen ist die Vorbereitung des GC auf die Rückforderung von Ressourcen, die sonst niemals gesammelt würden.
Ein Paradebeispiel sind Zirkelverweise.
Während es empfehlenswert ist, Muster zu verwenden, die Zirkelverweise vermeiden, kann dies die GC-Erfassung des übergeordneten Objekts stoppen, wenn Sie (z. B.) ein 'untergeordnetes' Objekt haben, das einen Verweis auf sein 'übergeordnetes' Objekt hat, wenn Sie es einfach abbrechen die Referenz und verlassen Sie sich auf GC - plus, wenn Sie einen Finalizer implementiert haben, wird es nie aufgerufen.
Die einzige Möglichkeit, dies zu umgehen, besteht darin, die Zirkelverweise manuell zu unterbrechen, indem die übergeordneten Verweise für die untergeordneten Verweise auf null gesetzt werden.
Die Implementierung von IDisposable für Eltern und Kinder ist der beste Weg, dies zu tun. Wenn Dispose für das übergeordnete Element aufgerufen wird, rufen Sie Dispose für alle untergeordneten Elemente auf und setzen Sie in der untergeordneten Dispose-Methode die übergeordneten Verweise auf null.
quelle
WeakReference
, überprüft das System ein Flag, das angibt, dass im letzten GC-Zyklus eine Live-Root-Referenz gefunden wurde und fügt das Objekt entweder einer Warteschlange von Objekten hinzu, die sofort finalisiert werden müssen, gibt das Objekt aus dem großen Objekthaufen frei oder macht die schwache Referenz ungültig. Zirkuläre Refs halten Objekte nicht am Leben, wenn keine anderen Refs vorhanden sind.Ich sehe, dass sich viele Antworten verschoben haben, um über die Verwendung von IDisposable für verwaltete und nicht verwaltete Ressourcen zu sprechen. Ich würde diesen Artikel als eine der besten Erklärungen vorschlagen, die ich gefunden habe, wie IDisposable tatsächlich verwendet werden sollte.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Für die eigentliche Frage; Wenn Sie IDisposable verwenden, um verwaltete Objekte zu bereinigen, die viel Speicherplatz beanspruchen, lautet die kurze Antwort Nein . Der Grund dafür ist, dass Sie, sobald Sie ein IDisposable entsorgen, es aus dem Geltungsbereich entfernen sollten. Zu diesem Zeitpunkt sind auch alle referenzierten untergeordneten Objekte außerhalb des Gültigkeitsbereichs und werden gesammelt.
Die einzige wirkliche Ausnahme wäre, wenn in verwalteten Objekten viel Speicher gebunden ist und Sie diesen Thread blockiert haben, bis ein Vorgang abgeschlossen ist. Wenn diese Objekte nach Abschluss dieses Aufrufs nicht benötigt werden, kann der Garbage Collector sie möglicherweise früher sammeln, wenn diese Verweise auf null gesetzt werden. Dieses Szenario würde jedoch einen schlechten Code darstellen, der überarbeitet werden musste - kein Anwendungsfall von IDisposable.
quelle