为什么c#垃圾收集器在请求得到满足之前不一直尝试释放内存

本文关键字:一直 内存 释放 满足 收集器 请求 为什么 | 更新日期: 2023-09-27 17:57:59

考虑以下代码:

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中,我创建了其中两个对象,释放一个,然后创建第三个。

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

我的问题是,为什么垃圾收集器不尽其所能来满足对内存的请求?如果它运行两次(一次是最终确定的,另一次是免费的),上面的代码会很好地工作。在抛出异常之前,垃圾收集器是否应该继续完成并收集,直到没有更多的内存可用为止?有没有任何方法可以将其配置为这种行为(在代码中或通过Visual Studio)?

为什么c#垃圾收集器在请求得到满足之前不一直尝试释放内存

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

如果在big1 = null之后添加此行。然而,您应该小心强制GC收集。除非你知道自己在做什么,否则不建议这样做。

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

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

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

中的垃圾收集。NET(代)

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