Jeg vet fra å ha lest Microsoft-dokumentasjonen at den "primære" bruken av IDisposable
-grensesnittet er å rydde opp i uadministrerte ressurser.
For meg betyr "unmanaged" ting som databasetilkoblinger, sockets, window handles osv. Men jeg har sett kode der Dispose()
-metoden er implementert for å frigjøre administrerte ressurser, noe som virker overflødig for meg, siden garbage collector burde ta seg av det for deg.
For eksempel:
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;
}
Spørsmålet mitt er om dette får garbage collector til å frigjøre minnet som brukes av MyCollection
raskere enn normalt?
redigert: Så langt har folk lagt ut noen gode eksempler på bruk av IDisposable for å rydde opp i uadministrerte ressurser som databasetilkoblinger og bitmaps. Men sett at _theList
i koden ovenfor inneholdt en million strenger, og du ville frigjøre minnet nå, i stedet for å vente på garbage collector. Ville koden ovenfor gjøre det?
IDisposable
brukes ofte for å utnytte using
-setningen og dra nytte av en enkel måte å foreta deterministisk opprydding av administrerte objekter på.
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.");
}
}
}
Jepp, den koden er fullstendig overflødig og unødvendig, og den får ikke søppelsamleren til å gjøre noe den ellers ikke ville gjort (når en forekomst av MyCollection går ut av scope, altså.) Spesielt .Clear()
-kallene.
Svar på redigeringen din: På en måte. Hvis jeg gjør dette:
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
Det er funksjonelt identisk med dette når det gjelder minnehåndtering:
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
Hvis du virkelig virkelig virkelig virkelig trenger å frigjøre minnet akkurat nå, kan du kalle GC.Collect()
. Det er imidlertid ingen grunn til å gjøre dette her. Minnet frigjøres når det trengs.
Hvis MyCollection
uansett kommer til å bli garbage collected, bør du ikke kaste den. Det vil bare tære mer på CPU-en enn nødvendig, og kan til og med ugyldiggjøre en forhåndsberegnet analyse som søppelsamleren allerede har utført.
Jeg bruker IDisposable
til å gjøre ting som å sikre at tråder avhendes korrekt, sammen med uadministrerte ressurser.
REDIGERT Som svar på Scotts kommentar:
Den eneste gangen GC-ytelsesmålingene påvirkes, er når GC.Collect() kalles".
Konseptuelt sett opprettholder GC en oversikt over objektreferansegrafen og alle referanser til den fra trådenes stabelrammer. Denne stakken kan være ganske stor og spenne over mange minnesider. Som en optimalisering mellomlagrer GC analysen av sider som sannsynligvis ikke endres så ofte, for å unngå å skanne siden på nytt unødvendig. GC mottar et varsel fra kjernen når data på en side endres, slik at den vet at siden er skitten og må skannes på nytt. Hvis samlingen er i Gen0, er det sannsynlig at andre ting på siden også endres, men dette er mindre sannsynlig i Gen1 og Gen2. Anekdotisk sett var disse krokene ikke tilgjengelige i Mac OS X for teamet som porterte GC til Mac for å få Silverlight-plugin-modulen til å fungere på denne plattformen.
Et annet argument mot unødvendig avhending av ressurser: Tenk deg en situasjon der en prosess losses. Tenk deg også at prosessen har kjørt en stund. Sannsynligvis har mange av prosessens minnesider blitt overført til disk. I det minste er de ikke lenger i L1- eller L2-hurtigbufferen. I en slik situasjon er det ikke noe poeng for en applikasjon som er i ferd med å laste ned, å swappe alle disse data- og kodesidene tilbake til minnet for å frigjøre ressurser som uansett vil bli frigjort av operativsystemet når prosessen avsluttes. Dette gjelder administrerte og til og med visse ikke-administrerte ressurser. Bare ressurser som holder liv i tråder som ikke er i bakgrunnen, må avhendes, ellers forblir prosessen i live.
Under normal kjøring finnes det flyktige ressurser som må ryddes opp på riktig måte (som @fezmonkey påpeker databasetilkoblinger, sockets, window handles) for å unngå uadministrerte minnelekkasjer. Det er slike ting som må avhendes. Hvis du oppretter en klasse som eier en tråd (og med eier mener jeg at den har opprettet den og derfor er ansvarlig for å sørge for at den stopper, i hvert fall etter min kodingsstil), så må den klassen mest sannsynlig implementere IDisposable
og rive ned tråden under Dispose
.
NET-rammeverket bruker grensesnittet IDisposable
som et signal, til og med en advarsel, til utviklere om at denne klassen må avhendes. Jeg kan ikke komme på noen typer i rammeverket som implementerer IDisposable
(bortsett fra eksplisitte grensesnittimplementeringer) der avhending er valgfritt.