为什么引用的对象是在c#中收集的?

时间:2013-12-16 22:04:52

标签: c# .net garbage-collection

我正在尝试使用这个c#代码来更好地理解.Net环境中的垃圾收集。此代码将无限数组分配到列表中,后台线程处理垃圾回收通知。垃圾收集工作当然可以通过不时检查load.C​​ount进行测试,并看到数字正在减少。我的问题实际上是为什么这些对象被垃圾收集?因为它们被列在一个列表中,所以它们仍被引用,并且根据我的理解,它们不应被收集。

代码示例取自MSDN。


 class Program
    {
        // Variable for continual checking in the
// While loop in the WaitForFullGCProc method. static bool checkForNotify = false;

// Variable for suspending work // (such servicing allocated server requests) // after a notification is received and then // resuming allocation after inducing a garbage collection. static bool bAllocate = false; // Variable for ending the example. static bool finalExit = false; // Collection for objects that // simulate the server request workload. static List<byte[]> load = new List<byte[]>(); public static void Main(string[] args) { try { // Register for a notification. GC.RegisterForFullGCNotification(10, 10); Console.WriteLine("Registered for GC notification."); checkForNotify = true; bAllocate = true; // Start a thread using WaitForFullGCProc. Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc)); thWaitForFullGC.Start(); // While the thread is checking for notifications in // WaitForFullGCProc, create objects to simulate a server workload. try { int lastCollCount = 0; int newCollCount = 0; while (true) { if (bAllocate) { load.Add(new byte[1000]); newCollCount = GC.CollectionCount(2); if (newCollCount != lastCollCount) { // Show collection count when it increases: Console.WriteLine("Gen 2 collection count: {0}", GC.CollectionCount(2).ToString()); lastCollCount = newCollCount; } // For ending the example (arbitrary). if (newCollCount == 500) { finalExit = true; checkForNotify = false; break; } } } } catch (OutOfMemoryException) { Console.WriteLine("Out of memory."); } finalExit = true; checkForNotify = false; GC.CancelFullGCNotification(); } catch (InvalidOperationException invalidOp) { Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled.\n" + invalidOp.Message); } } public static void OnFullGCApproachNotify() { Console.WriteLine("Redirecting requests."); // Method that tells the request queuing // server to not direct requests to this server. RedirectRequests(); // Method that provides time to // finish processing pending requests. FinishExistingRequests(); // This is a good time to induce a GC collection // because the runtime will induce a full GC soon. // To be very careful, you can check precede with a // check of the GC.GCCollectionCount to make sure // a full GC did not already occur since last notified. GC.Collect(); Console.WriteLine("Induced a collection."); } public static void OnFullGCCompleteEndNotify() { // Method that informs the request queuing server // that this server is ready to accept requests again. AcceptRequests(); Console.WriteLine("Accepting requests again."); } public static void WaitForFullGCProc() { while (true) { // CheckForNotify is set to true and false in Main. while (checkForNotify) { // Check for a notification of an approaching collection. GCNotificationStatus s = GC.WaitForFullGCApproach(); if (s == GCNotificationStatus.Succeeded) { Console.WriteLine("GC Notification raised."); OnFullGCApproachNotify(); } else if (s == GCNotificationStatus.Canceled) { Console.WriteLine("GC Notification cancelled."); break; } else { // This can occur if a timeout period // is specified for WaitForFullGCApproach(Timeout) // or WaitForFullGCComplete(Timeout) // and the time out period has elapsed. Console.WriteLine("GC Notification not applicable."); break; } // Check for a notification of a completed collection. s = GC.WaitForFullGCComplete(); if (s == GCNotificationStatus.Succeeded) { Console.WriteLine("GC Notifiction raised."); OnFullGCCompleteEndNotify(); } else if (s == GCNotificationStatus.Canceled) { Console.WriteLine("GC Notification cancelled."); break; } else { // Could be a time out. Console.WriteLine("GC Notification not applicable."); break; } } Thread.Sleep(500); // FinalExit is set to true right before // the main thread cancelled notification. if (finalExit) { break; } } } private static void RedirectRequests() { // Code that sends requests // to other servers. // Suspend work. bAllocate = false; } private static void FinishExistingRequests() { // Code that waits a period of time // for pending requests to finish. // Clear the simulated workload. load.Clear(); } private static void AcceptRequests() { // Code that resumes processing // requests on this server. // Resume work. bAllocate = true; } }

1 个答案:

答案 0 :(得分:3)

以下是我修改过的程序版本:

class Program
{
    List<byte[]> myJunkList = new List<byte[]>();

    static void Main(string[] args)
    {
        var program = new Program();
        program.Run();
    }

    public void Run()
    {
        GC.RegisterForFullGCNotification(10, 10);

        var pollingThread = new Thread(() =>
        {
            while (true)
            {
                var gcStatus = GC.WaitForFullGCApproach(1000);

                switch (gcStatus)
                {
                    case GCNotificationStatus.Succeeded:
                        Console.WriteLine("GC has started.");
                        break;
                }

                gcStatus = GC.WaitForFullGCComplete(1000);

                switch (gcStatus)
                {
                    case GCNotificationStatus.Succeeded:
                        Console.WriteLine("GC has ended.");
                        break;
                }

                Thread.Sleep(500);
            }
        });
        pollingThread.Start();

        AllocateMemory();

        GC.CancelFullGCNotification();
    }

    void AllocateMemory()
    {
        var rand = new Random();
        var newCount = 0;
        var oldCount = 0;

        while (true)
        {
            var junk = new byte[1024];
            rand.NextBytes(junk);

            myJunkList.Add(junk);

            newCount = GC.CollectionCount(2);

            if (newCount != oldCount)
            {
                Console.WriteLine("GC 2 has run {0} times (list now has {1} items).", newCount, myJunkList.Count);
            }
            oldCount = newCount;
        }
    }

输出显示:

GC has started.
GC 2 has run 1 times (list now has 14935 items).
GC has ended.
GC 2 has run 2 times (list now has 33146 items).
GC 2 has run 3 times (list now has 55403 items).
GC 2 has run 4 times (list now has 95877 items).
GC 2 has run 5 times (list now has 166723 items).
GC has ended.
GC 2 has run 6 times (list now has 292218 items).
GC has ended.
GC has ended.
GC 2 has run 7 times (list now has 539165 items).
GC has ended.
GC has ended.
GC has ended.
GC 2 has run 8 times (list now has 986512 items).
GC has ended.
GC has ended.
GC has started.
GC has ended.
GC has started.

如您所见,列表正在收集,最终您会按预​​期获得OutOfMemoryException