Share via


記憶體回收告知

更新: 2008 年 7 月

在某些情況下,Common Language Runtime 執行完整記憶體回收可能會降低效能。特別是處理大量要求的伺服器,這可能會造成問題,因為在這種情況下,長時間的記憶體回收可能會導致要求逾時。為避免完整回收在重要期間發生,您可由系統通知完整記憶體回收即將進行,然後採取動作將工作負載重新導向至另一個伺服器執行個體。您也可以自行引發回收,只要目前伺服器執行個體不需要處理要求即可。

注意事項:

此功能只有在並行記憶體回收停用時才能使用。並行記憶體回收預設為啟用,除非您是在裝載環境中執行,由主應用程式為您變更了組態。當使用伺服器記憶體回收時,無法使用並行記憶體回收。此功能不支援並行記憶體回收,因為並行記憶體回收進行的同時允許記憶體配置。如需如何停用並行記憶體回收的詳細資訊,請參閱 <gcConcurrent> 執行階段設定。

RegisterForFullGCNotification 方法會註冊通知,當執行階段偵測到完整記憶體回收即將進行時,即發出通知。此通知分為兩部分:何時即將進行完整記憶體回收,以及完整記憶體回收何時完成。

若要判斷何時發出通知,請使用 WaitForFullGCApproachWaitForFullGCComplete 方法。一般來說,您可以在 while 迴圈 (Loop) 中使用這些方法,以持續取得顯示通知狀態的 GCNotificationStatus 列舉型別 (Enumeration)。如果該值為 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> 集合。

    最後再引發記憶體回收,因為工作負載變輕。

  • OfFullGCCompeteNotify

    此方法會呼叫使用者方法 AcceptRequests 以繼續接受要求,因為伺服器不再容易受到完整記憶體回收的影響。模擬此動作的方式是將 bAllocate 變數設為 true,這樣物件就會繼續加入到 List<T> 集合。

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

' Variables for continual checking in the
' While loop in the WaitForFullGcProc method.
Private Shared checkForNotify As Boolean = False

' Variable for suspending work 
' (such as servicing allocated server requests)
' after a notification is received and then 
' resuming allocation after inducing a garbage collection.
Private Shared bAllocate As Boolean = False

' Variable for ending the example.
Private Shared finalExit As Boolean = False

' Collection for objects that  
' simulate the server request workload.
Private Shared load As New List(Of Byte())


Public Shared Sub Main(ByVal args() As String)
    Try
        ' Register for a notification. 
        GC.RegisterForFullGCNotification(10, 10)
        Console.WriteLine("Registered for GC notification.")

        bAllocate = True
        checkForNotify = True

        ' Start a thread using WaitForFullGCProc.
        Dim thWaitForFullGC As Thread = _
            New Thread(New ThreadStart(AddressOf WaitForFullGCProc))
        thWaitForFullGC.Start()

        ' While the thread is checking for notifications in
        ' WaitForFullGCProc, create objects to simulate a server workload.
        Try
            Dim lastCollCount As Integer = 0
            Dim newCollCount As Integer = 0


            While (True)
                If bAllocate = True Then

                    load.Add(New Byte(1000) {})
                    newCollCount = GC.CollectionCount(2)
                    If (newCollCount <> lastCollCount) Then
                        ' Show collection count when it increases:
                        Console.WriteLine("Gen 2 collection count: {0}", _
                                          GC.CollectionCount(2).ToString)
                        lastCollCount = newCollCount
                    End If

                    ' For ending the example (arbitrary).
                    If newCollCount = 500 Then
                        finalExit = True
                        checkForNotify = False
                        bAllocate = False
                        Exit While
                    End If

                End If
            End While

        Catch outofMem As OutOfMemoryException
            Console.WriteLine("Out of memory.")
        End Try

        finalExit = True
        checkForNotify = False
        GC.CancelFullGCNotification()

    Catch invalidOp As InvalidOperationException
        Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled." _
                          & vbLf & invalidOp.Message)
    End Try
End Sub

// 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);
    }
}

下列程式碼包含 WaitForFullGCProc 使用者方法,此方法包含持續進行的 while 迴圈,檢查是否有記憶體回收通知。

Public Shared Sub WaitForFullGCProc()

    While True
        ' CheckForNotify is set to true and false in Main.

        While checkForNotify
            ' Check for a notification of an approaching collection.
            Dim s As GCNotificationStatus = GC.WaitForFullGCApproach
            If (s = GCNotificationStatus.Succeeded) Then
                Console.WriteLine("GC Notifiction raised.")
                OnFullGCApproachNotify()
            ElseIf (s = GCNotificationStatus.Canceled) Then
                Console.WriteLine("GC Notification cancelled.")
                Exit While
            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.")
                Exit While
            End If

            ' Check for a notification of a completed collection.
            s = GC.WaitForFullGCComplete
            If (s = GCNotificationStatus.Succeeded) Then
                Console.WriteLine("GC Notifiction raised.")
                OnFullGCCompleteEndNotify()
            ElseIf (s = GCNotificationStatus.Canceled) Then
                Console.WriteLine("GC Notification cancelled.")
                Exit While
            Else
                ' Could be a time out.
                Console.WriteLine("GC Notification not applicable.")
                Exit While
            End If

        End While
        Thread.Sleep(500)
        ' FinalExit is set to true right before  
        ' the main thread cancelled notification.
        If finalExit Then
            Exit While
        End If

    End While
End Sub

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 Notifiction 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;
        }
    }

}

下列程式碼包含 OnFullGCApproachNotify 方法,此方法是從

WaitForFullGCProc 方法呼叫的。

Public Shared Sub 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 ful 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.")
End Sub

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 方法呼叫的。

Public Shared Sub OnFullGCCompleteEndNotify()
    ' Method that informs the request queuing server
    ' that this server is ready to accept requests again.
    AcceptRequests()
    Console.WriteLine("Accepting requests again.")
End Sub

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

下列程式碼包含從 OnFullGCApproachNotify 和 OnFullGCCompleteNotify 方法呼叫的使用者方法。這些使用者方法會重新導向要求、完成現有的要求,然後在完整記憶體回收執行之後繼續要求。

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;

}

完整的程式碼範例如下:

Imports System
Imports System.Collections.Generic
Imports System.Threading
Imports Microsoft.VisualBasic



Class Program


    ' Variables for continual checking in the
    ' While loop in the WaitForFullGcProc method.
    Private Shared checkForNotify As Boolean = False

    ' Variable for suspending work 
    ' (such as servicing allocated server requests)
    ' after a notification is received and then 
    ' resuming allocation after inducing a garbage collection.
    Private Shared bAllocate As Boolean = False

    ' Variable for ending the example.
    Private Shared finalExit As Boolean = False

    ' Collection for objects that  
    ' simulate the server request workload.
    Private Shared load As New List(Of Byte())


    Public Shared Sub Main(ByVal args() As String)
        Try
            ' Register for a notification. 
            GC.RegisterForFullGCNotification(10, 10)
            Console.WriteLine("Registered for GC notification.")

            bAllocate = True
            checkForNotify = True

            ' Start a thread using WaitForFullGCProc.
            Dim thWaitForFullGC As Thread = _
                New Thread(New ThreadStart(AddressOf WaitForFullGCProc))
            thWaitForFullGC.Start()

            ' While the thread is checking for notifications in
            ' WaitForFullGCProc, create objects to simulate a server workload.
            Try
                Dim lastCollCount As Integer = 0
                Dim newCollCount As Integer = 0


                While (True)
                    If bAllocate = True Then

                        load.Add(New Byte(1000) {})
                        newCollCount = GC.CollectionCount(2)
                        If (newCollCount <> lastCollCount) Then
                            ' Show collection count when it increases:
                            Console.WriteLine("Gen 2 collection count: {0}", _
                                              GC.CollectionCount(2).ToString)
                            lastCollCount = newCollCount
                        End If

                        ' For ending the example (arbitrary).
                        If newCollCount = 500 Then
                            finalExit = True
                            checkForNotify = False
                            bAllocate = False
                            Exit While
                        End If

                    End If
                End While

            Catch outofMem As OutOfMemoryException
                Console.WriteLine("Out of memory.")
            End Try

            finalExit = True
            checkForNotify = False
            GC.CancelFullGCNotification()

        Catch invalidOp As InvalidOperationException
            Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled." _
                              & vbLf & invalidOp.Message)
        End Try
    End Sub

    Public Shared Sub 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 ful 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.")
    End Sub

    Public Shared Sub OnFullGCCompleteEndNotify()
        ' Method that informs the request queuing server
        ' that this server is ready to accept requests again.
        AcceptRequests()
        Console.WriteLine("Accepting requests again.")
    End Sub

    Public Shared Sub WaitForFullGCProc()

        While True
            ' CheckForNotify is set to true and false in Main.

            While checkForNotify
                ' Check for a notification of an approaching collection.
                Dim s As GCNotificationStatus = GC.WaitForFullGCApproach
                If (s = GCNotificationStatus.Succeeded) Then
                    Console.WriteLine("GC Notifiction raised.")
                    OnFullGCApproachNotify()
                ElseIf (s = GCNotificationStatus.Canceled) Then
                    Console.WriteLine("GC Notification cancelled.")
                    Exit While
                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.")
                    Exit While
                End If

                ' Check for a notification of a completed collection.
                s = GC.WaitForFullGCComplete
                If (s = GCNotificationStatus.Succeeded) Then
                    Console.WriteLine("GC Notifiction raised.")
                    OnFullGCCompleteEndNotify()
                ElseIf (s = GCNotificationStatus.Canceled) Then
                    Console.WriteLine("GC Notification cancelled.")
                    Exit While
                Else
                    ' Could be a time out.
                    Console.WriteLine("GC Notification not applicable.")
                    Exit While
                End If

            End While
            Thread.Sleep(500)
            ' FinalExit is set to true right before  
            ' the main thread cancelled notification.
            If finalExit Then
                Exit While
            End If

        End While
    End Sub

    Private Shared Sub RedirectRequests()
        ' Code that sends requests
        ' to other servers.

        ' Suspend work.
        bAllocate = False
    End Sub

    Private Shared Sub FinishExistingRequests()
        ' Code that waits a period of time
        ' for pending requests to finish.

        ' Clear the simulated workload.
        load.Clear()

    End Sub

    Private Shared Sub AcceptRequests()
        ' Code that resumes processing
        ' requests on this server.

        ' Resume work.
        bAllocate = True
    End Sub
End Class
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 Notifiction 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;

        }
    }
}

請參閱

其他資源

記憶體回收

變更記錄

日期

記錄

原因

2008 年 7 月

加入主題。

SP1 功能變更。