Share via


垃圾回收通知

更新: 2008 年 7 月

公共语言运行时执行的完整垃圾回收有时可能会对性能产生负面影响。对于处理大量请求的服务器而言,这个问题尤为突出;在这种情况下,长时间的垃圾回收可能会导致请求超时。若要防止在重要时间段发生完整垃圾回收,可以让系统通知您即将发生完整垃圾回收,然后您可以采取措施将工作负荷重定向到其他服务器实例。您也可以在当前服务器实例不需要处理请求时自己引发回收。

说明:

仅当禁用并发垃圾回收时才能使用此功能。默认情况下会启用并发垃圾回收,除非您在宿主环境中运行应用程序且宿主已为您更改了该配置。在使用服务器垃圾回收时,并发垃圾回收不可用。此功能不支持并发垃圾回收,原因是在进行并发垃圾回收期间允许进行内存分配。有关如何禁用并发垃圾回收的信息,请参见 <gcConcurrent> 运行时设置。

RegisterForFullGCNotification 方法注册一个通知,当运行时察觉到即将发生完整垃圾回收时将引发该通知。此通知包含两部分:何时将发生完整垃圾回收以及完整垃圾回收何时完成。

若要确定何时引发了通知,请使用 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> 集合来模拟此操作。

    最后,会由于工作负荷变轻而引发垃圾回收。

  • 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 功能更改。