9

考虑下面的代码:

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)?

4

2 回答 2

2

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

如果在big1 = null. 但是,您应该小心强制 GC 收集。除非您知道自己在做什么,否则不建议这样做。

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

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

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

.NET 中的垃圾收集(世代)

于 2013-02-06T08:22:14.747 回答
0

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

于 2013-02-06T08:15:04.907 回答