Ich habe ein Problem mit finalisierbaren Objekten festgestellt, das nicht von GC
erfasst wird, wenn Dispose()
nicht explizit aufgerufen wurde. Ich weiß, dass ich Dispose()
explizit aufrufen sollte, wenn ein Objekt IDisposable
implementiert, aber ich dachte immer, dass es sicher ist, sich auf Framework zu verlassen, und wenn ein Objekt referenziert wird, kann es gesammelt werden.Warum wird Objekt mit Finalizer nicht erfasst, auch wenn es nicht gerootet wird?
Aber nach einigen Versuchen mit windbg/sos/sosex Ich habe festgestellt, dass, wenn GC.SuppressFinalize() nicht für finalizable Objekt aufgerufen wurde es nicht gesammelt hat, auch wenn es unbewurzelten wird. Wenn Sie also endliche Objekte (DbConnection, FileStream, etc.) ausgiebig verwenden und diese nicht explizit freigeben, können Sie auf einen zu hohen Speicherverbrauch oder sogar auf OutOfMemoryException
stoßen.
Hier ist eine Beispielanwendung:
public class MemoryTest
{
private HundredMegabyte hundred;
public void Run()
{
Console.WriteLine("ready to attach");
for (var i = 0; i < 100; i++)
{
Console.WriteLine("iteration #{0}", i + 1);
hundred = new HundredMegabyte();
Console.WriteLine("{0} object was initialized", hundred);
Console.ReadKey();
//hundred.Dispose();
hundred = null;
}
}
static void Main()
{
var test = new MemoryTest();
test.Run();
}
}
public class HundredMegabyte : IDisposable
{
private readonly Megabyte[] megabytes = new Megabyte[100];
public HundredMegabyte()
{
for (var i = 0; i < megabytes.Length; i++)
{
megabytes[i] = new Megabyte();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~HundredMegabyte()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
}
public override string ToString()
{
return String.Format("{0}MB", megabytes.Length);
}
}
public class Megabyte
{
private readonly Kilobyte[] kilobytes = new Kilobyte[1024];
public Megabyte()
{
for (var i = 0; i < kilobytes.Length; i++)
{
kilobytes[i] = new Kilobyte();
}
}
}
public class Kilobyte
{
private byte[] bytes = new byte[1024];
}
Auch nach 10 Iterationen Sie, dass der Speicherverbrauch zu hoch ist (von 700 MB bis 1 GB) und bekommt sogar noch höher mit mehr Iterationen finden. Nach dem Anhängen an den Prozess mit WinDBG können Sie feststellen, dass alle großen Objekte nicht verwaltet, aber nicht gesammelt werden.
Situation ändert sich, wenn Sie SuppressFinalize()
explizit aufrufen: Speicherverbrauch ist stabil um 300-400MB auch unter Hochdruck und WinDBG zeigt, dass es keine unrooted Objekte gibt, Speicher ist frei.
Die Frage ist also: Ist es ein Fehler im Framework? Gibt es eine logische Erklärung?
Weitere Einzelheiten:
nach jeder Iteration, windbg zeigt, dass:
- Finalisierungsschlange leer
- freachable Warteschlange leer ist
- Generation 2-Objekte enthält (Hundert) aus früheren Iterationen
- Objekte aus früheren Iterationen sind nicht entfernt
Ich habe versucht, es zu laufen, und ich habe es bis zu 700mb, an dem Punkt es auf 100mb oder so von selbst abgeschnitten. (Ich habe einfach weiter gedrückt und beobachtete die Speicherauslastung im Task-Manager). Running Windows 8. – keyboardP
Sehr interessant ... Ich habe es auf win7 x64 getestet. Die ersten 10 Iterationen mit einer Sekunde Pause, danach einfach einen beliebigen Schlüssel halten ... outofmemory – 6opuc
Noch einmal versucht, aber zu 100 Iterationen ohne Ausnahme. Auch 64-Bit, aber ziemlich interessant. Vielleicht kann jemand anderes auch Ergebnisse testen und veröffentlichen. – keyboardP