考虑下面的代码:

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 gigs),则在创建第三个BigObject时会抛出内存不足异常。这是因为,当第三次请求内存时,无法满足该请求,因此垃圾回收器将运行。但是,准备好收集的第一个BigObject具有终结器方法,因此将其而不是被收集到终结队列中并进行终结。然后,垃圾收集器停止运行,并引发异常。但是,如果未注释对GC.Collect的调用,或者删除了finalize方法,则代码将正常运行。

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

最佳答案

它何时运行GC并尝试回收内存并不确定。

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

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

Best Practice for Forcing Garbage Collection in C#

When should I use GC.SuppressFinalize()?

Garbage collection in .NET (generations)

10-07 20:10