Rozważmy poniższy kod:Dlaczego moduł czyszczący C# nie próbuje dalej zwolnić pamięci, dopóki żądanie nie zostanie spełnione?
using System;
namespace memoryEater
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("alloc 1");
var big1 = new BigObject();
Console.WriteLine("alloc 2");
var big2 = new BigObject();
Console.WriteLine("null 1");
big1 = null;
//GC.Collect();
Console.WriteLine("alloc3");
big1 = new BigObject();
Console.WriteLine("done");
Console.Read();
}
}
public class BigObject
{
private const uint OneMeg = 1024 * 1024;
private static int _idCnt;
private readonly int _myId;
private byte[][] _bigArray;
public BigObject()
{
_myId = _idCnt++;
Console.WriteLine("BigObject {0} creating... ", _myId);
_bigArray = new byte[700][];
for (int i = 0; i < 700; i++)
{
_bigArray[i] = new byte[OneMeg];
}
for (int j = 0; j < 700; j++)
{
for (int i = 0; i < OneMeg; i++)
{
_bigArray[j][i] = (byte)i;
}
}
Console.WriteLine("done");
}
~BigObject()
{
Console.WriteLine("BigObject {0} finalised", _myId);
}
}
}
Mam klasy, BigObject, który tworzy tablicę 700MiB w jej konstruktora i ma metodę sfinalizować który robi nic innego niż druk na konsoli. W opcji Główne tworzę dwa z tych obiektów, jeden wolny, a następnie trzeci.
Jeśli zostanie skompilowany dla 32 bitów (w celu ograniczenia pamięci do 2 gigów), wyjątek braku pamięci zostanie zgłoszony podczas tworzenia trzeciego obiektu BigObject. Dzieje się tak dlatego, że gdy pamięć jest żądana po raz trzeci, żądanie nie może zostać spełnione, a zatem moduł do zbierania śmieci działa. Jednak pierwszy BigObject, który jest gotowy do zebrania, ma metodę finalizatora, więc zamiast być zbierany jest umieszczany w kolejce finalizacji i jest finalizowany. Śmieciarz zatrzymuje się, a wyjątek zostaje rzucony. Jeśli jednak odwołanie do GC.Collect zostanie odkomentowane lub metoda finalizacji zostanie usunięta, kod będzie działał poprawnie.
Moje pytanie brzmi: dlaczego niszczarka śmieci nie robi wszystkiego, aby spełnić prośbę o pamięć? Jeśli uruchomił się dwa razy (raz do sfinalizowania i ponownie do uwolnienia) powyższy kod działałby dobrze. Czy nie powinien on kontynuować gromadzenia śmieci i zbierać, dopóki nie będzie można zwolnić pamięci przed wyrzuceniem wyjątku, i czy istnieje sposób, aby skonfigurować to zachowanie tak (w kodzie lub poprzez Visual Studio)?
Problem spowodowany efektem ubocznym w finalizatora. Nie rób tego! – leppie
Natknąłem się na to kilka lat temu i napisałem post na blogu. Zobacz: http://xacc.wordpress.com/2011/02/22/gc-suppressfinalize/ (zobacz także komentarze) – leppie
Interesujące, ale kod nadal nie powiedzie się, jeśli Konsola.WriteLine zostanie usunięta z finalizatora. –