Ler em inglês

Partilhar via


Notificações de coleta de lixo

Há situações em que uma coleta de lixo completa (ou seja, uma coleção de geração 2) pelo common language runtime pode afetar negativamente o desempenho. Isso pode ser um problema, particularmente com servidores que processam grandes volumes de solicitações; Nesse caso, uma longa coleta de lixo pode causar um tempo limite de solicitação. Para evitar que uma coleta completa ocorra durante um período crítico, você pode ser notificado de que uma coleta de lixo completa está se aproximando e, em seguida, tomar medidas para redirecionar a carga de trabalho para outra instância do servidor. Você também pode induzir uma coleta por conta própria, desde que a instância atual do servidor não precise processar solicitações.

O RegisterForFullGCNotification método registra para que uma notificação seja gerada quando o tempo de execução deteta que uma coleta de lixo completa está se aproximando. Há duas partes nessa notificação: quando a coleta completa de lixo está se aproximando e quando a coleta completa de lixo foi concluída.

Aviso

Quando o <elemento de configuração gcConcurrent> estiver habilitado, WaitForFullGCComplete poderá retornar NotApplicable GCNotificationStatus se o GC completo tiver sido feito como um GC em segundo plano.

Para determinar quando uma notificação foi gerada, use os WaitForFullGCApproach métodos e WaitForFullGCComplete . Normalmente, você usa esses métodos em um while loop para obter continuamente uma GCNotificationStatus enumeração que mostra o status da notificação. Se esse valor for Succeeded, você poderá fazer o seguinte:

  • Em resposta a uma notificação obtida com o WaitForFullGCApproach método, você pode redirecionar a carga de trabalho e, possivelmente, induzir uma coleta por conta própria.

  • Em resposta a uma notificação obtida com o WaitForFullGCComplete método, você pode disponibilizar a instância do servidor atual para processar solicitações novamente. Você também pode coletar informações. Por exemplo, você pode usar o CollectionCount método para registrar o número de coleções.

Os WaitForFullGCApproach e os WaitForFullGCComplete métodos são projetados para trabalhar juntos. Usar um sem o outro pode produzir resultados indeterminados.

Recolha Completa de Lixo

O tempo de execução causa uma coleta de lixo completa quando qualquer um dos seguintes cenários for verdadeiro:

  • Memória suficiente foi promovida para a geração 2 para causar a próxima coleção de geração 2.

  • Memória suficiente foi promovida para a pilha de objetos grande para causar a próxima geração 2 coleção.

  • Uma coleção da geração 1 é escalada para uma coleção da geração 2 devido a outros fatores.

Os limites especificados no RegisterForFullGCNotification método aplicam-se aos dois primeiros cenários. No entanto, no primeiro cenário, você nem sempre receberá a notificação no momento proporcional aos valores de limite especificados por dois motivos:

  • O tempo de execução não verifica cada alocação de objeto pequeno (por motivos de desempenho).

  • Apenas as coleções da geração 1 promovem a memória para a geração 2.

O terceiro cenário também contribui para a incerteza de quando receberá a notificação. Embora isso não seja uma garantia, ele se mostra uma maneira útil de mitigar os efeitos de uma coleta de lixo completa inoportuna, redirecionando as solicitações durante esse tempo ou induzindo a coleta por conta própria quando ela pode ser melhor acomodada.

Parâmetros de limite de notificação

O RegisterForFullGCNotification método tem dois parâmetros para especificar os valores limite dos objetos de geração 2 e o heap de objeto grande. Quando esses valores forem atingidos, uma notificação de coleta de lixo deve ser levantada. A tabela a seguir descreve esses parâmetros.

Parâmetro Description
maxGenerationThreshold Um número entre 1 e 99 que especifica quando a notificação deve ser levantada com base nos objetos promovidos na geração 2.
largeObjectHeapThreshold Um número entre 1 e 99 que especifica quando a notificação deve ser gerada com base nos objetos alocados no heap de objeto grande.

Se você especificar um valor muito alto, há uma alta probabilidade de receber uma notificação, mas pode ser um período muito longo para esperar antes que o tempo de execução cause uma coleção. Se você mesmo induzir uma coleção, poderá recuperar mais objetos do que seriam recuperados se o tempo de execução causar a coleção.

Se você especificar um valor muito baixo, o tempo de execução poderá causar a coleta antes que você tenha tido tempo suficiente para ser notificado.

Exemplo

Description

No exemplo a seguir, um grupo de servidores atende solicitações da Web de entrada. Para simular a carga de trabalho de processamento de solicitações, matrizes de bytes são adicionadas a uma List<T> coleção. Cada servidor se registra para uma notificação de coleta de lixo e, em seguida, inicia um thread no WaitForFullGCProc método de usuário para monitorar continuamente a GCNotificationStatus enumeração que é retornada pelo WaitForFullGCApproach e os WaitForFullGCComplete métodos.

Os WaitForFullGCApproach métodos e os WaitForFullGCComplete chamam seus respetivos métodos de usuário de manipulação de eventos quando uma notificação é gerada:

  • OnFullGCApproachNotify

    Esse método chama o RedirectRequests método de usuário, que instrui o servidor de enfileiramento de solicitações a suspender o envio de solicitações para o servidor. Isso é simulado definindo a variável bAllocate de nível de classe para false que mais nenhum objeto seja alocado.

    Em seguida, o FinishExistingRequests método de usuário é chamado para concluir o processamento das solicitações pendentes do servidor. Isso é simulado limpando a List<T> coleção.

    Finalmente, uma coleta de lixo é induzida porque a carga de trabalho é leve.

  • OnFullGCCompleteNotify

    Esse método chama o método AcceptRequests de usuário para continuar aceitando solicitações porque o servidor não é mais suscetível a uma coleta de lixo completa. Essa ação é simulada definindo a variável como para true que os objetos possam voltar a bAllocate ser adicionados à List<T> coleção.

O código a seguir contém o Main método do exemplo.

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

O código a seguir contém o WaitForFullGCProc método user, que contém um loop while contínuo para verificar notificações de coleta de lixo.

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

O código a seguir contém o OnFullGCApproachNotify método como chamado a partir do

WaitForFullGCProc método.

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

O código a seguir contém o OnFullGCApproachComplete método como chamado a partir do

WaitForFullGCProc método.

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

O código a seguir contém os métodos de usuário que são chamados a OnFullGCApproachNotify partir dos métodos e OnFullGCCompleteNotify . Os métodos de usuário redirecionam solicitações, concluem solicitações existentes e, em seguida, retomam solicitações após a ocorrência de uma coleta de lixo completa.

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

O exemplo de código inteiro é o seguinte:

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

Consulte também