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

Sea*_*eid 9 c# garbage-collection finalizer

请考虑以下代码:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

sa_*_*213 0

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