为什么c#垃圾收集器在满足请求之前不会继续尝试释放内存?

考虑以下代码:

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); } } } 

我有一个类BigObject,它在它的构造函数中创建一个700MiB数组,并且有一个finalize方法除了打印到控制台之外什么都不做。 在Main中,我创建了两个这样的对象,free one,然后创建第三个。

如果将其编译为32位(以便将内存限制为2 gigs),则在创建第三个BigObject时会抛出内存不足exception。 这是因为,当第三次请求内存时,无法满足请求,因此垃圾收集器运行。 但是,第一个准备收集的BigObject具有终结器方法,因此不会被收集而是被置于终结队列中并最终确定。 垃圾收集器然后停止并抛出exception。 但是,如果取消对GC.Collect的调用,或者删除了finalize方法,则代码将正常运行。

我的问题是,为什么垃圾收集器不能尽其所能来满足内存请求? 如果它运行两次(一次完成并再次释放),上述代码将正常工作。 垃圾收集器是否应该继续完成并收集,直到在抛出exception之前不再有内存可用,并且是否有任何方法可以将其配置为以这种方式运行(在代码中还是通过Visual Studio)?

当GC工作并尝试回收内存时,它是不确定的。

如果在big1 = null之后添加此行。 但是你应该对强制GC收集感到困惑。 除非你知道你在做什么,否则不推荐它。

 GC.Collect(); GC.WaitForPendingFinalizers(); 

在C#中强制垃圾收集的最佳实践

我什么时候应该使用GC.SuppressFinalize()?

.NET中的垃圾收集(代)

我猜它是因为在垃圾收集期间终结器执行的时间是未定义的。 不保证在任何特定时间释放资源(除非调用Close方法或Dispose方法。),终结器运行的顺序也是随机的,所以你可以在另一个对象上等待终结器,而你的对象等待。