请考虑以下代码:
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时会引发内存不足异常。这是因为,当第三次请求内存时,无法满足请求,因此垃圾收集器运行。但是,第一个准备收集的BigObject具有终结器方法,因此不会被收集,而是放在终结队列中并最终确定。垃圾收集器然后停止并抛出异常。但是,如果取消对GC.Collect的调用,或者删除了finalize方法,则代码将正常运行。
我的问题是,为什么垃圾收集器不能尽其所能来满足内存请求?如果它运行两次(一次完成并再次释放),上面的代码将正常工作。垃圾收集器是否应该继续完成并收集,直到在抛出异常之前不再有内存可用,并且有没有办法将它配置为以这种方式运行(在代码中或通过Visual Studio)?
答案 0 :(得分:2)
当GC工作并尝试回收内存时,它不确定。
如果您在big1 = null
之后添加此行。但是,您应该对强制GC收集感到困惑。除非你知道自己在做什么,否则不推荐它。
GC.Collect();
GC.WaitForPendingFinalizers();
Best Practice for Forcing Garbage Collection in C#
答案 1 :(得分:0)
我猜是因为在垃圾收集期间终结器执行的时间是未定义的。不保证在任何特定时间释放资源(除非调用Close方法或Dispose方法。),终结器的运行顺序也是随机的,所以你可以在另一个对象上等待终结器,而你的对象等待