Hãy xem xét các đoạn mã sau:Tại sao bộ thu gom rác C# không tiếp tục cố gắng giải phóng bộ nhớ cho đến khi yêu cầu có thể được thỏa mãn?
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);
}
}
}
Tôi có một lớp học, BigObject, mà tạo ra một mảng 700MiB trong constructor của nó, và có một phương thức Finalize mà không làm gì khác hơn là in ra màn hình. Trong Main, tôi tạo ra hai đối tượng này, một đối tượng miễn phí và sau đó tạo một đối tượng thứ ba.
Nếu điều này được biên dịch cho 32 bit (để giới hạn bộ nhớ thành 2 hợp đồng biểu diễn), ngoại lệ hết bộ nhớ được ném khi tạo BigObject thứ ba. Điều này là do, khi bộ nhớ được yêu cầu lần thứ ba, yêu cầu không thể được đáp ứng và do đó bộ thu gom rác sẽ chạy. Tuy nhiên, BigObject đầu tiên, đã sẵn sàng để được thu thập, có một phương pháp finaliser vì vậy thay vì được thu thập được đặt trên hàng đợi cuối cùng và được hoàn thành. Người thu gom rác sau đó dừng lại và ngoại lệ được ném ra. Tuy nhiên, nếu cuộc gọi đến GC.Collect là không được chú ý, hoặc phương thức hoàn thành được loại bỏ, mã sẽ chạy tốt.
Câu hỏi của tôi là, tại sao bộ thu gom rác không thực hiện mọi thứ có thể để đáp ứng yêu cầu bộ nhớ? Nếu nó chạy hai lần (một lần để hoàn thành và một lần nữa để miễn phí) mã trên sẽ làm việc tốt. Không nên bộ gom rác tiếp tục hoàn thành và thu thập cho đến khi không còn bộ nhớ nào có thể được giải phóng trước khi ném ngoại lệ, và có cách nào để cấu hình nó hoạt động theo cách này (hoặc bằng mã hoặc thông qua Visual Studio) không?
Vấn đề là do tác dụng phụ trong finalizer. Đừng làm thế! – leppie
Tôi đã xem qua một vài năm trở lại và viết một bài đăng trên blog. Xem: http://xacc.wordpress.com/2011/02/22/gc-suppressfinalize/ (xem chú thích) – leppie
Thú vị, nhưng mã vẫn thất bại nếu Console.WriteLine được lấy ra khỏi finaliser. –