【发布时间】:2013-01-21 08:35:54
【问题描述】:
考虑下面的代码:
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)?
【问题讨论】:
-
问题是由终结器中的副作用引起的。不要那样做!
-
几年前我也遇到过这个问题并写了一篇博文。见:xacc.wordpress.com/2011/02/22/gc-suppressfinalize(也见 cmets)
-
有趣,但如果 Console.WriteLine 从终结器中删除,代码仍然会失败。
标签: c# garbage-collection finalizer