以下のコードを検討してください。
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);
}
}
}
コンストラクターで 700MiB 配列を作成するクラス BigObject があり、コンソールに出力する以外に何もしない finalize メソッドがあります。Main では、これらのオブジェクトを 2 つ作成し、1 つを解放してから、3 つ目を作成します。
これが 32 ビット用にコンパイルされている場合 (メモリーを 2 GB に制限するため)、3 番目の BigObject の作成時にメモリー不足の例外がスローされます。これは、メモリが 3 回目に要求されたときに要求を満たすことができず、ガベージ コレクタが実行されるためです。ただし、収集の準備ができている最初の BigObject にはファイナライザー メソッドがあるため、収集される代わりにファイナライズ キューに配置され、ファイナライズされます。その後、ガベージ コレクターが停止し、例外がスローされます。ただし、GC.Collect への呼び出しがコメント化されていない場合、または finalize メソッドが削除されている場合、コードは正常に実行されます。
私の質問は、ガベージ コレクターがメモリの要求を満たすためにできる限りのことをしないのはなぜですか? 2 回実行した場合 (1 回はファイナライズ、2 回目は解放)、上記のコードは正常に動作します。ガベージ コレクターは、例外をスローする前にメモリを解放できなくなるまでファイナライズと収集を続けるべきではありませんか? また、このように動作するように構成する方法はありますか (コードまたは Visual Studio を介して)。