閱讀英文

共用方式為


記憶體回收告知

在某些情況下,通用語言執行平台 (CLR) 所執行的完整記憶體回收 (也就是層代 2 回收) 可能會降低效能。 這是一個問題,特別會發生在處理大量要求的伺服器上;在此情況下,完整記憶體回收可能會導致要求逾時。若要避免在關鍵期間發生完整回收,您可以在接近完整記憶體回收時收到通知,然後採取行動將工作負載重新導向至另一個伺服器執行個體。 您也可以自行引發回收,前提是目前的伺服器執行個體不需要處理要求。

RegisterForFullGCNotification 方法會註冊一個當執行階段偵測到接近完整記憶體回收時要引發的通知。 通知有兩個部分:當接近完整記憶體回收時,以及當完整記憶體回收完成時。

警告

啟用 <gcConcurrent> 設定元素時,如果以背景 GC 形式進行完整 GC,WaitForFullGCComplete 可能會傳回 NotApplicableGCNotificationStatus

若要判斷引發通知的時機,請使用 WaitForFullGCApproachWaitForFullGCComplete 方法。 一般來說,您會在 while 迴圈中使用這些方法,以持續取得可顯示通知狀態的 GCNotificationStatus 列舉。 如果值為 Succeeded,您可以執行以下動作:

  • 為了回應使用 WaitForFullGCApproach 方法取得的通知,您可以重新導向工作負載,並有可能自行引發回收。

  • 為了回應使用 WaitForFullGCComplete 方法取得的通知,您可以讓目前的伺服器執行個體可用以重新處理要求。 您也可以收集資訊。 例如,您可以使用 CollectionCount 方法來記錄回收的數目。

WaitForFullGCApproachWaitForFullGCComplete 方法是設計來搭配使用。 僅使用其中一個可能會產生不定的結果。

完整記憶體回收

當下列任何案例都成立時,執行階段會導致完整記憶體回收:

  • 足夠的記憶體已升階至層代 2,導致下一個層代 2 回收。

  • 足夠的記憶體已升階至大型物件堆積,導致下一個層代 2 回收。

  • 因為其他因素,層代 1 回收已擴大為層代 2 回收。

您在 RegisterForFullGCNotification 方法中指定的閾值會套用到前兩個案例。 不過,在第一個案例中,您不一定會在指定的閾值成比例的時間收到通知,原因有兩個:

  • 執行階段不會檢查每個小型物件配置 (基於效能考量)。

  • 只有層代 1 回收會將記憶體升階為層代 2。

第三個案例也會造成您收到通知之時間的不確定性。 雖然這不是一種保證,但是透過在這段期間將要求重新導向或在更能容納的時候自行引發,確實已證明這是一種減輕不適當的完整記憶體回收之效果的有用方法。

通知閾值參數

RegisterForFullGCNotification 方法有兩個參數來指定層代 2 物件和大型物件堆積的閾值。 當符合那些值時,應該會引發記憶體回收通知。 下列表格描述這些參數。

參數 描述
maxGenerationThreshold 範圍從 1 到 99 的數字,指定何時應根據層代 2 中所升階的物件來引發通知。
largeObjectHeapThreshold 範圍從 1 到 99 的數字,指定何時應根據大型物件堆積中所配置的物件來引發通知。

如果您指定的值太高,則有很高的機率會收到通知,但是在執行階段引發回收之前,可能會有一段很長的等待期間。 如果您自行引發回收,則可能會回收比原本回收 (如果執行階段導致回收的話) 還要多的物件。

如果您指定的值太低,執行階段可能會在您有足夠的時間收到通知之前導致回收。

範例

描述

在下列範例中,一組伺服器會處理傳入的 Web 要求。 若要模擬處理要求的工作負載,請將位元組陣列加入至 List<T> 集合。 每部伺服器都會註冊記憶體回收通知,然後在 WaitForFullGCProc 使用者方法上啟動執行緒以持續監視 WaitForFullGCApproachWaitForFullGCComplete 方法所傳回的 GCNotificationStatus 列舉。

當引發通知時,WaitForFullGCApproachWaitForFullGCComplete 方法會呼叫各自的事件處理使用者方法:

  • OnFullGCApproachNotify

    這個方法會呼叫 RedirectRequests 使用者方法,此使用者方法會指示要求佇列伺服器暫停將要求傳送至伺服器。 這會透過將類別層級變數 bAllocate 設定為 false 來進行模擬,如此便不會再配置任何物件。

    接下來,會呼叫 FinishExistingRequests 使用者方法來完成處理擱置中伺服器要求。 這會透過清除 List<T> 集合來進行模擬。

    最後,因為工作負載減輕,所以會引發記憶體回收。

  • OnFullGCCompleteNotify

    此方法會呼叫使用者方法 AcceptRequests 以繼續接受要求,因為伺服器不再容許完整記憶體回收。 此動作是透過將 bAllocate 變數設定為 true 來進行模擬,讓物件能夠繼續加入到 List<T> 集合。

下列程式碼包含範例的 Main 方法。

C#
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);
    }
}

下列程式碼包含 WaitForFullGCProc 使用者方法,其中包含用來檢查記憶體回收通知的連續 while 迴圈。

C#
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.
            GCNotificationStatus status = GC.WaitForFullGCComplete();
            if (status == GCNotificationStatus.Succeeded)
            {
                Console.WriteLine("GC Notification raised.");
                OnFullGCCompleteEndNotify();
            }
            else if (status == 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;
        }
    }
}

下列程式碼包含從下列方法呼叫的 OnFullGCApproachNotify 方法:

WaitForFullGCProc 方法。

C#
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.");
}

下列程式碼包含從下列方法呼叫的 OnFullGCApproachComplete 方法:

WaitForFullGCProc 方法。

C#
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.");
}

下列程式碼包含從 OnFullGCApproachNotifyOnFullGCCompleteNotify 方法呼叫的使用者方法。 使用者方法會將要求重新導向,然後在完整記憶體回收之後繼續要求。

C#
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;
}

完整的程式碼範例如下所示:

C#
using System;
using System.Collections.Generic;
using System.Threading;

namespace GCNotify
{
    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.
                    GCNotificationStatus status = GC.WaitForFullGCComplete();
                    if (status == GCNotificationStatus.Succeeded)
                    {
                        Console.WriteLine("GC Notification raised.");
                        OnFullGCCompleteEndNotify();
                    }
                    else if (status == 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;
        }
    }
}

另請參閱