Jeg ved fra læsning af [Microsoft-dokumentationen] (https://docs.microsoft.com/dotnet/api/system.idisposable), at den primære anvendelse af IDisposable
-interfacet er at rydde op i uadministrerede ressourcer.
For mig betyder "unmanaged" ting som databaseforbindelser, sockets, vindueshåndtag osv. Men jeg har set kode, hvor metoden Dispose()
er implementeret til at frigøre managed ressourcer, hvilket forekommer mig overflødigt, da garbage collector burde tage sig af det for dig.
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;
}
Mit spørgsmål er, om dette får garbage collector til at frigøre den hukommelse, der bruges af MyCollection
, hurtigere end den normalt ville gøre?
redigering: Indtil videre har folk postet nogle gode eksempler på at bruge IDisposable til at rydde op i uadministrerede ressourcer såsom databaseforbindelser og bitmaps. Men lad os antage, at _theList
i ovenstående kode indeholdt en million strenge, og at du ville frigøre den hukommelse nu, i stedet for at vente på garbage collector. Ville ovenstående kode kunne gøre det?
IDisposable
bruges ofte til at udnytte using
-erklæringen og drage fordel af en nem måde at foretage deterministisk oprydning af administrerede 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.");
}
}
}
Jep, den kode er fuldstændig overflødig og unødvendig, og den får ikke skraldemanden til at gøre noget, den ellers ikke ville gøre (når en instans af MyCollection går ud af scope, dvs.), især .Clear()
-opkaldene.
Svar på din redigering: På en måde. Hvis jeg gø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 funktionelt identisk med dette med henblik på hukommelsesstyring:
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 har brug for at frigøre hukommelsen lige nu, så kald GC.Collect()
. Der er dog ingen grund til at gøre det her. Hukommelsen vil blive frigjort, når der er brug for den.
Hvis MyCollection
alligevel bliver indsamlet i skraldespanden, er det ikke nødvendigt at bortskaffe den. Hvis du gør det, vil det blot sætte CPU'en mere i sving end nødvendigt, og det kan endda ugyldiggøre en forudberegnet analyse, som garbage collector allerede har udført.
Jeg bruger IDisposable
til at gøre ting som at sikre, at tråde bortskaffes korrekt, sammen med uadministrerede ressourcer.
EDIT Som svar på Scott's kommentar:
Det eneste tidspunkt, hvor GC-præstationsmålingerne påvirkes, er, når der foretages et kald til [sic] GC.Collect()"_
Begrebsmæssigt opretholder GC en visning af objektreferencegrafen og alle referencer til den fra threads' stack frames. Denne heap kan være ret stor og strække sig over mange sider af hukommelsen. Som en optimering lagrer GC sin analyse af sider, der sandsynligvis ikke vil blive ændret særlig ofte, i en cache for at undgå unødvendig ny scanning af siden. GC modtager en meddelelse fra kernen, når data på en side ændres, så den ved, at siden er snavset og kræver en ny scanning. Hvis samlingen er i Gen0, er det sandsynligt, at andre ting på siden også ændres, men det er mindre sandsynligt i Gen1 og Gen2. Anekdotisk set var disse hooks ikke tilgængelige i Mac OS X for det team, der porterede GC til Mac for at få Silverlight-plugin'et til at fungere på denne platform.
Endnu et punkt mod unødvendig bortskaffelse af ressourcer: Forestil dig en situation, hvor en proces aflastes. Forestil dig også, at processen har kørt i et stykke tid. Der er stor sandsynlighed for, at mange af processens hukommelsessider er blevet swappet til disken. I det mindste er de ikke længere i L1- eller L2-cachen. I en sådan situation er der ingen grund til, at et program, der er ved at aflade, skal bytte alle disse data- og kodesider tilbage til hukommelsen for at frigive ressourcer, som operativsystemet alligevel vil frigive, når processen afsluttes. Dette gælder for administrerede og endda visse ikke-administrerede ressourcer. Kun ressourcer, der holder tråde, der ikke er i baggrunden, i live, skal bortskaffes, ellers forbliver processen i live.
Under normal udførelse er der nu flygtige ressourcer, der skal ryddes korrekt op (som @fezmonkey påpeger databaseforbindelser, sockets, vindueshåndtag) for at undgå uadministrerede hukommelseslækager. Det er den slags ting, der skal bortskaffes. Hvis du opretter en klasse, der ejer en tråd (og med ejer mener jeg, at den har oprettet den og derfor er ansvarlig for at sikre, at den stopper, i hvert fald efter min kodningsstil), så skal denne klasse højst sandsynligt implementere IDisposable
og rive tråden ned under Dispose
.
.NET-rammen bruger IDisposable
-grænsefladen som et signal, ja endog en advarsel, til udviklerne om, at denne klasse skal bortskaffes. Jeg kan ikke komme i tanke om nogen typer i rammen, der implementerer IDisposable
(bortset fra eksplicitte grænsefladeimplementeringer), hvor bortskaffelse er valgfri.