Compartilhar via


Desempenho e a coleta de lixo

Este tópico descreve problemas relacionados ao uso de memória e coleta de lixo. Ele aborda questões que pertencem a heap gerenciada e explica como minimizar o efeito de coleta de lixo em seus aplicativos. Cada problema tem links para procedimentos que podem ser usados para investigar problemas.

Este tópico contém as seções a seguir:

  • Ferramentas de análise de desempenho

  • Solucionar problemas de desempenho

  • Diretrizes para solução de problemas

  • Procedimentos de verificação de desempenho

Ferramentas de análise de desempenho

As seções a seguir descrevem as ferramentas que estão disponíveis para investigar problemas de coleta de lixo e o uso de memória. O procedimentos fornecida posteriormente neste tópico se referir a essas ferramentas.

Contadores de desempenho de memória

Você pode usar os contadores de desempenho para coletar dados de desempenho. Para obter instruções, consulte Perfil Runtime. A.Categoria de memória do CLR NET de contadores de desempenho, conforme descrito em Contadores de desempenho de memória, fornece informações sobre o coletor de lixo.

Depuradores de SOS

Você pode usar o O depurador do Windows (WinDbg) ou o depurador de Visual Studio com o SOS. dll (SOS extensão de depuração) para inspecionar os objetos no heap gerenciado.

Para instalar o WinDbg, instale o Debugging Tools for Windows do site WDK e ferramentas de desenvolvedor. Para obter informações sobre como usar a extensão de depuração SOS, consulte Como: Use SOS.

Eventos ETW de coleta de lixo

Eventos para Windows (ETW) é um sistema de rastreamento que complementam a criação de perfil de rastreamento e depuração de suporte fornecido pelo.NET Framework. Começando com o .NET Framework versão 4, eventos ETW de coleta de lixo captura informações úteis para analisar o heap gerenciado a partir de um ponto de vista estatístico. Por exemplo, o GCStart_V1 evento, que é disparado quando uma coleta de lixo está prestes a ocorrer, fornece as seguintes informações:

  • A geração de objetos está sendo coletada.

  • O que disparou a coleta de lixo.

  • Tipo de coleta de lixo (simultâneo ou não simultâneo).

O log de eventos ETW é eficiente e não irá mascarar os problemas de desempenho associados à coleta de lixo. Um processo pode fornecer seus próprios eventos em conjunto com eventos ETW. Quando conectado, os eventos do aplicativo e os eventos de coleta de lixo podem ser correlacionados para determinar como e quando ocorrem de problemas de heap. Por exemplo, um aplicativo de servidor poderia fornecer eventos no início e no final de uma solicitação do cliente.

A API de criação de perfil

Common language runtime (CLR) perfil interfaces fornecem informações detalhadas sobre os objetos que foram afetados durante a coleta de lixo. Um gerador de perfil pode ser notificado quando uma coleta de lixo inicia e termina. Ele pode fornecer relatórios sobre os objetos no heap gerenciado, incluindo uma identificação de objetos em cada geração. Para obter mais informações, consulte A API de criação de perfil de rastreamento de objeto.

Geradores de perfis podem fornecer informações abrangentes. No entanto, geradores de perfis complexos potencialmente podem modificar o comportamento do aplicativo.

Monitoramento de recursos do domínio de aplicativo

Começando com o .NET Framework 4, o recurso de domínio de aplicativo de monitoramento (ARM) permite que os hosts monitorar o uso de CPU e memória por domínio de aplicativo. Para obter mais informações, consulte Monitoramento de recursos do domínio de aplicativo.

Voltar ao topo

Solucionar problemas de desempenho

A primeira etapa é determinar se o problema é realmente a coleta de lixo. Se você determinar que é, selecione na lista a seguir para solucionar o problema.

  • Uma exceção de falta de memória é lançada.

  • O processo usa muita memória

  • O coletor de lixo não recupera objetos rápido o suficiente

  • O heap gerenciado está muito fragmentado

  • Pausa de coleta de lixo é muito longa

  • Geração 0 é muito grande

  • O uso da CPU durante a coleta de lixo é muito alto

Problema: Uma exceção de falta de memória é lançada.

Há dois casos legítimos para um gerenciado OutOfMemoryException seja lançada:

  • Com memória virtual.

    O coletor de lixo aloca a memória do sistema em segmentos de um tamanho predefinido. Se uma alocação requer um segmento adicional, mas não há nenhum bloco contíguo livre, à esquerda no espaço de memória virtual do processo, a alocação de heap gerenciado falhará.

  • Não ter memória física suficiente para alocar.

Verificações de desempenho

Determine se a exceção de falta de memória é gerenciada.

Determine a quantidade de memória virtual pode ser reservada.

Determine se há memória física suficiente.

Se você determinar que a exceção não é legítima, entre em contato com o Atendimento Microsoft e suporte com as seguintes informações:

  • A pilha com a exceção de falta de memória gerenciada.

  • Despejo de memória completo.

  • Dados que prova que ele não é uma exceção legítima de falta de memória, incluindo dados mostra que a memória física ou virtual não não um problema.

Problema: O processo usa muita memória

Uma suposição comum é que o uso de memória exibir no desempenho o guia do Gerenciador de tarefas do Windows pode indicar quando muita memória está sendo usada. No entanto, que exibem pertence ao conjunto de trabalho; ele não fornece informações sobre o uso de memória virtual.

Se você determinar que o problema é causado pelo heap gerenciado, você deve medir o heap gerenciado ao longo do tempo para determinar os padrões.

Se você determinar que o problema não é causado pelo heap gerenciado, você deve usar a depuração nativa.

Verificações de desempenho

Determine a quantidade de memória virtual pode ser reservada.

Determine quanta memória heap gerenciado está empenhando.

Determine quanta memória reserva do heap gerenciado.

Determine os objetos grandes na geração 2.

Determine as referências a objetos.

Problema: O coletor de lixo não recupera objetos rápido o suficiente

Quando ela aparecer como se não estão sendo recuperados objetos conforme o esperado para a coleta de lixo, você deve determinar se há quaisquer referências fortes a esses objetos.

Você também pode encontrar esse problema não se tiver havido nenhuma coleta de lixo para a geração que contém um objeto inativo, o que indica que o finalizador do objeto inativo não foi executado. Por exemplo, isso é possível quando você estiver executando um aplicativo single-threaded apartment (STA) e o thread que a fila de finalizadores não é possível chamar os serviços para ele.

Verificações de desempenho

Verifique as referências a objetos.

Determine se um finalizador foi executado.

Determine se há objetos que estão aguardando para ser finalizado.

Problema: O Managed Heap É muito fragmentado

O nível de fragmentação é calculado como a proporção de espaço livre sobre o total de memória alocada para a geração. Para a geração 2, um nível aceitável de fragmentação é não mais de 20%. Porque a geração 2 pode obter muito grande, a taxa de fragmentação é mais importante do que o valor absoluto.

Tendo bastante espaço livre na geração 0 não é um problema porque esta é a geração onde novos objetos são alocados.

Sempre a fragmentação ocorre no heap de objeto grande porque ele não é compactado. Os objetos livres que são adjacentes são naturalmente recolhidos em um único espaço para satisfazer às solicitações de alocação de objeto grande.

A fragmentação pode se tornar um problema na geração 1 e a geração 2. Se essas gerações tem uma grande quantidade de espaço livre após a coleta de lixo, uso de objeto do aplicativo pode precisar de modificação, e você deve considerar a reavaliar o tempo de vida dos objetos de longo prazo.

Excesso de fixação de objetos pode aumentar a fragmentação. Se a fragmentação é alta, muitos objetos podem ser fixados.

Se a fragmentação da memória virtual está impedindo que o coletor de lixo adicionando segmentos, as causas poderiam ser um destes procedimentos:

  • Carregamento e descarregamento dos muitos assemblies pequenos freqüente.

  • Mantendo muitas referências a objetos COM, quando interoperar com código não gerenciado.

  • Criação de grandes objetos transitórios, que faz com que a pilha de objetos grandes alocar e liberar segmentos de pilha com freqüência.

    Quando a hospedagem do CLR, um aplicativo pode solicitar que o coletor de lixo reter seus segmentos. Isso reduz a freqüência de alocações de segmento. Isso é realizado usando-se o sinalizador STARTUP_HOARD_GC_VM na Enumeração de STARTUP_FLAGS.

Verificações de desempenho

Determine a quantidade de espaço livre no heap gerenciado.

Determine o número de objetos fixados.

Se você acha que não há nenhuma causa legítima para a fragmentação, entre em contato com o Atendimento Microsoft e suporte.

Problema: Pausa de coleta de lixo é muito longa

Coleta de lixo opera em tempo real suave, portanto, um aplicativo deve ser capaz de tolerar algumas pausas. Um critério para o tempo de real suave é que 95% das operações devem terminar no prazo.

Na coleta de lixo simultâneas, threads gerenciados podem ser executados durante uma coleta, o que significa que as pausas são mínimas.

Coleções de lixo efêmera (gerações 0 e 1) por último a apenas alguns milissegundos, diminuir assim a pausa geralmente não é viável. No entanto, você pode diminuir as pausas nas coleções de geração 2, alterando o padrão de solicitações de alocação por um aplicativo.

Outro, mais preciso, o método é usar eventos ETW de coleta de lixo. Você pode encontrar os intervalos para coleções adicionando as diferenças de carimbo de data / hora para uma seqüência de eventos. A seqüência da coleção inteira inclui a suspensão do mecanismo de execução, a coleta de lixo e a retomada do mecanismo de execução.

Você pode usar Notificações de coleta de lixo para determinar se um servidor está prestes a ter uma coleção de geração 2, e se solicitações de redirecionamento para outro servidor podem facilitar a quaisquer problemas com pausas.

Verificações de desempenho

Determine o período de tempo em uma coleta de lixo.

Determine o que causou uma coleta de lixo.

Problema: Geração 0 É muito grande

Geração 0 é provável que um número maior de objetos em um sistema de 64 bits, especialmente quando você usar a coleta de lixo do servidor em vez de coleta de lixo da estação de trabalho. Isso ocorre porque o limite para acionar uma coleta de lixo 0 de geração é maior nesses ambientes e coleções de geração 0 podem obter muito maiores. Melhoria no desempenho quando um aplicativo aloca mais memória antes de uma coleta de lixo é disparada.

Problema: O uso da CPU durante a coleta de lixo é muito alto

Uso da CPU serão alto durante uma coleta de lixo. Se uma quantidade significativa de tempo de processamento é gasto em uma coleta de lixo, o número de coleções é freqüente demais ou a coleção é duradouras e muito longo. Uma taxa de aumento de alocação de objetos no heap gerenciado faz com que a coleta de lixo para ocorrer com mais freqüência. Diminuir a taxa de alocação reduz a freqüência das coletas de lixo.

Você pode monitorar as taxas de alocação usando o Allocated Bytes/second contador de desempenho. Para obter mais informações, consulte Contadores de desempenho de memória.

A duração de uma coleção é principalmente um fator do número de objetos que sobrevivem após a alocação. O coletor de lixo deve passar por uma grande quantidade de memória persistam muitos objetos a serem coletados. O trabalho para compactar os sobreviventes é demorado. Para determinar quantos objetos foram tratados durante uma coleta, defina um ponto de interrupção no depurador do final de uma coleta de lixo para uma geração especificada.

Verificações de desempenho

Determine se o alto uso da CPU é causado pela coleta de lixo.

Defina um ponto de interrupção no final da coleta de lixo.

Voltar ao topo

Diretrizes para solução de problemas

Esta seção descreve as diretrizes que devem ser consideradas como começar seus investigações.

Estação de trabalho ou a coleta de lixo do servidor

Determine se você estiver usando o tipo correto de coleta de lixo. Se o seu aplicativo usa vários threads e instâncias de objeto, use a coleta de lixo do servidor em vez de coleta de lixo da estação de trabalho. Coleta de lixo do servidor funciona em vários segmentos, enquanto a coleta de lixo da estação de trabalho requer várias instâncias de um aplicativo para executar seus próprios segmentos de coleta de lixo e competir pelo tempo de CPU.

Um aplicativo que tem uma carga baixa e que realiza tarefas com pouca freqüência em segundo plano, como, por exemplo, um serviço, poderia usar a coleta de lixo da estação de trabalho com a coleta de lixo simultâneas desabilitada.

Quando medir o tamanho do Heap gerenciado

A menos que você estiver usando um gerador de perfil, você terá que estabelecer um padrão consistente de medição para efetivamente diagnosticar problemas de desempenho. Considere os seguintes pontos para estabelecer uma agenda:

  • Se você medir após uma coleta de lixo da geração 2, toda a heap gerenciada estará livre de lixo (objetos inativos).

  • Se você medir imediatamente após uma coleta de lixo da geração 0, os objetos em gerações 1 e 2 não serão coletados ainda.

  • Se você medir imediatamente antes de uma coleta de lixo, medirá quanto alocação possível antes de inicia a coleta de lixo.

  • Medindo durante uma coleta de lixo é problemática, pois as estruturas de dados do coletor de lixo não estão em um estado válido para passagem e não ser capazes de oferecer os resultados completos. Isso ocorre por design.

  • Quando você estiver usando a coleta de lixo da estação de trabalho com coleta de lixo simultâneas, os objetos recuperados não são compactados, para que o tamanho da pilha pode ser igual ou maior (fragmentação pode fazer parecer maior).

  • Coleta de lixo de simultâneas na geração 2 está atrasada quando a carga de memória física é muito alta.

O procedimento a seguir descreve como definir um ponto de interrupção para que você pode medir o heap gerenciado.

Para definir um ponto de interrupção no final da coleta de lixo

  • No WinDbg com a extensão de depurador SOS carregada, digite o seguinte comando:

    bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"

    onde GcCondemnedGeneration está definido como geração desejado. Este comando requer símbolos privados.

    Este comando força uma quebra de se RestartEE é executado depois que os objetos de geração 2 foram recuperados para coleta de lixo.

    Coleta de lixo do servidor, apenas um thread chama RestartEE, portanto, o ponto de interrupção ocorrerá somente uma vez durante uma coleta de lixo de geração 2.

Voltar ao topo

Procedimentos de verificação de desempenho

Esta seção descreve os procedimentos a seguir para isolar a causa do seu problema de desempenho:

  • Determine se o problema é causado pela coleta de lixo.

  • Determine se a exceção de falta de memória é gerenciada.

  • Determine a quantidade de memória virtual pode ser reservada.

  • Determine se há memória física suficiente.

  • Determine quanta memória heap gerenciado está empenhando.

  • Determine quanta memória reserva do heap gerenciado.

  • Determine os objetos grandes na geração 2.

  • Determine as referências a objetos.

  • Determine se um finalizador foi executado.

  • Determine se há objetos que estão aguardando para ser finalizado.

  • Determine a quantidade de espaço livre no heap gerenciado.

  • Determine o número de objetos fixados.

  • Determine o período de tempo em uma coleta de lixo.

  • Determine o que disparou uma coleta de lixo.

  • Determine se o alto uso da CPU é causado pela coleta de lixo.

Para determinar se o problema é causado pela coleta de lixo

  • Examine os contadores de desempenho de dois memória seguintes:

    • % De tempo no GC. Exibe a porcentagem de tempo decorrido que foi gasto executando uma coleta de lixo após o último ciclo de coleta de lixo. Use este contador para determinar se o coletor de lixo está gastando muito tempo para disponibilizar espaço do heap gerenciado. Se o tempo gasto na coleta de lixo é relativamente baixo, que poderia indicar um problema de recurso fora do heap gerenciado. Esse contador pode não ser exato quando simultâneos ou coleta de lixo do plano de fundo está envolvida.

    • N º total de Bytes confirmados. Exibe a quantidade de memória virtual, confirmada atualmente pelo garbage collector. Use este contador para determinar se a memória consumida pelo coletor de lixo é uma parte excessiva da memória que o aplicativo usa.

    A maioria da os contadores de desempenho de memória são atualizados no final de cada coleta de lixo. Portanto, eles podem não refletir as atuais condições que você deseja obter informações sobre.

Para determinar se a exceção de falta de memória é gerenciada

  1. No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite a exceção de impressão (pe) comando:

    !pe

    Se a exceção for gerenciada, OutOfMemoryException é exibido como o tipo de exceção, conforme mostrado no exemplo a seguir.

    Exception object: 39594518
    Exception type: System.OutOfMemoryException
    Message: <none>
    InnerException: <none>
    StackTrace (generated):
    
  2. Se a saída não especificar uma exceção, você precisará determinar qual thread a exceção de falta de memória é de. Digite o seguinte comando no depurador para mostrar todos os threads com suas pilhas de chamadas:

    ~*kb

    O segmento com a pilha de chamadas de exceção é indicado pela RaiseTheException argumento. Esse é o objeto de exceção gerenciada.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0 
    
  3. Você pode usar o comando a seguir para despejar as exceções aninhadas.

    !pe -nested

    Se você não encontrar quaisquer exceções, originou a exceção de falta de memória do código não gerenciado.

Para determinar a quantidade de memória virtual pode ser reservada.

  • No WinDbg com a extensão de depurador SOS carregada, digite o seguinte comando para obter a maior região livre:

    !address -summary

    A maior região livre é exibida conforme mostrado na saída a seguir.

    Largest free region: Base 54000000 - Size 0003A980
    

    Neste exemplo, o tamanho do maior região livre é de aproximadamente 24000 KB (3A980 em hexadecimal). Essa região é muito menor que o coletor de lixo precisa de um segmento.

    - ou -

  • Use o vmstat comando:

    !vmstat

    A maior região livre é o maior valor na coluna máximo, conforme mostrado na saída a seguir.

    TYPE        MINIMUM   MAXIMUM     AVERAGE   BLK COUNT   TOTAL
    ~~~~        ~~~~~~~   ~~~~~~~     ~~~~~~~   ~~~~~~~~~~  ~~~~
    Free:
    Small       8K        64K         46K       36          1,671K
    Medium      80K       864K        349K      3           1,047K
    Large       1,384K    1,278,848K  151,834K  12          1,822,015K
    Summary     8K        1,278,848K  35,779K   51          1,824,735K
    

Para determinar se há memória física suficiente

  1. Inicie o Gerenciador de tarefas do Windows.

  2. Sobre o desempenho guia, observe o valor de compromisso. (No Windows 7, examine Confirmar (KB) no o grupo de sistema.)

    Se o Total está próximo a limite de, você está com pouca memória física.

Para determinar a quantidade de memória heap gerenciado está empenhando

  • Use o # Total committed bytes contador de desempenho de memória para obter o número de bytes que o heap gerenciado está empenhando. O coletor de lixo confirma blocos em um segmento, conforme necessário, nem todos ao mesmo tempo.

    Observação

    Não use o # Bytes in all Heaps contador de desempenho, porque ela não o uso de memória real representam pelo heap gerenciado.O tamanho de uma geração está incluído neste valor e, na verdade, seu tamanho de limite, ou seja, o tamanho induz a coleta de lixo, se a geração é preenchida com objetos.Portanto, esse valor geralmente é zero.

Para determinar a quantidade de memória heap gerenciado reserva

  • Use o # Total reserved bytes contador de desempenho de memória.

    O coletor de lixo reserva de memória em segmentos, e você pode determinar onde um segmento é iniciado usando o eeheap comando.

  • No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando:

    !eeheap -gc

    O resultado é o seguinte.

    Number of GC Heaps: 2
    ------------------------------
    Heap 0 (002db550)
    generation 0 starts at 0x02abe29c
    generation 1 starts at 0x02abdd08
    generation 2 starts at 0x02ab0038
    ephemeral segment allocation context: none
     segment    begin allocated     size
    02ab0000 02ab0038  02aceff4 0x0001efbc(126908)
    Large object heap starts at 0x0aab0038
     segment    begin allocated     size
    0aab0000 0aab0038  0aab2278 0x00002240(8768)
    Heap Size   0x211fc(135676)
    ------------------------------
    Heap 1 (002dc958)
    generation 0 starts at 0x06ab1bd8
    generation 1 starts at 0x06ab1bcc
    generation 2 starts at 0x06ab0038
    ephemeral segment allocation context: none
     segment    begin allocated     size
    06ab0000 06ab0038  06ab3be4 0x00003bac(15276)
    Large object heap starts at 0x0cab0038
     segment    begin allocated     size
    0cab0000 0cab0038  0cab0048 0x00000010(16)
    Heap Size    0x3bbc(15292)
    ------------------------------
    GC Heap Size   0x24db8(150968)
    

    Os endereços indicados por "segmento" são os endereços iniciais dos segmentos.

Para determinar os objetos grandes na geração 2

  • No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando:

    !dumpheap –stat

    Se o heap gerenciado é grande, dumpheap pode levar algum tempo para concluir.

    Você pode começar a analisar pelas últimas algumas linhas de saída, pois listam os objetos que usam mais espaço. Por exemplo:

    2c6108d4   173712     14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo
    00155f80      533     15216804      Free
    7a747c78   791070     15821400 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700930     19626040 System.Collections.Specialized.ListDictionary
    2c64e36c    78644     20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo
    79124228   121143     29064120 System.Object[]
    035f0ee4    81626     35588936 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    791242ec    40182     90664128 System.Collections.Hashtable+bucket[]
    790fa3e0  3154024    137881448 System.String
    Total 8454945 objects
    

    O último objeto listado é uma seqüência de caracteres e ocupa mais espaço. Você pode examinar o seu aplicativo para ver como os objetos de cadeia de caracteres podem ser otimizados. Para ver a strings que são entre 150 e 200 bytes, digite o seguinte:

    !dumpheap -type System.String -min 150 -max 200

    Um exemplo dos resultados é o seguinte.

    Address  MT           Size  Gen
    1875d2c0 790fa3e0      152    2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11
    …
    

    Usando um número inteiro em vez de uma seqüência de caracteres para uma identificação pode ser mais eficiente. Se a mesma seqüência está sendo repetida milhares de vezes, considere a possibilidade de interning de seqüência de caracteres. Para obter mais informações sobre o interning de seqüência de caracteres, consulte o tópico de referência para o String.Intern método.

Para determinar as referências a objetos

  • No WinDbg com a extensão de depurador SOS carregada, digite o seguinte comando para referências a objetos da lista:

    !gcroot

    -or-

  • Para determinar as referências para um objeto específico, incluem o endereço:

    !gcroot 1c37b2ac

    Raízes de pilhas podem ser falsos positivos. Para obter mais informações, use o comando !help gcroot.

    ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)->
    19010b78(DemoApp.FormDemoApp)->
    19011158(System.Windows.Forms.PropertyStore)->
    … [omitted]
    1c3745ec(System.Data.DataTable)->
    1c3747a8(System.Data.DataColumnCollection)->
    1c3747f8(System.Collections.Hashtable)->
    1c376590(System.Collections.Hashtable+bucket[])->
    1c376c98(System.Data.DataColumn)->
    1c37b270(System.Data.Common.DoubleStorage)->
    1c37b2ac(System.Double[])
    Scan Thread 0 OSTHread 99c
    Scan Thread 6 OSTHread 484
    

    O gcroot comando pode levar muito tempo para concluir. Qualquer objeto que não é recuperado pela coleta de lixo é um objeto vivo. Isso significa que alguns raiz é diretamente ou indiretamente mantiver assim, o objeto gcroot deve retornar informações de caminho para o objeto. Você deve examinar os gráficos retornados e ver por que esses objetos são ainda referenciados.

Para determinar se um finalizador tiver sido executado.

  • Execute um programa de teste que contém o código a seguir:

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    

    Se o teste resolve o problema, isso significa que o coletor de lixo não estava recuperando objetos, pois os finalizadores para aqueles objetos tinham sido suspenso. O GC.WaitForPendingFinalizers método permite que os finalizadores concluir suas tarefas e corrige o problema.

Para determinar se há objetos que estão aguardando para ser finalizado.

  1. No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando:

    !finalizequeue

    Examine o número de objetos que estão prontos para finalização. Se o número for alto, você deve examinar o motivo pelo qual esses finalizadores não é possível em todos os de progresso ou não é possível progresso rápido suficiente.

  2. Para obter uma saída de threads, digite o seguinte comando:

    threads -special

    Esse comando fornece a saída como a seguir.

           OSID     Special thread type
        2    cd0    DbgHelper 
        3    c18    Finalizer 
        4    df0    GC SuspendEE 
    

    O thread do finalizador indica qual finalizador, se houver, está sendo executada. Quando um thread do finalizador não estiver executando qualquer finalizadores, está aguardando um evento para orientá-lo a fazer seu trabalho. Na maioria das vezes você verá o thread do finalizador neste estado porque ele é executado em THREAD_HIGHEST_PRIORITY e o término da execução de finalizadores, se houver, deve muito rapidamente.

Para determinar a quantidade de espaço livre no heap gerenciado

  • No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando:

    !dumpheap -type Free -stat

    Este comando exibe o tamanho total de todos os objetos livres no heap gerenciado, conforme mostrado no exemplo a seguir.

    total 230 objects
    Statistics:
          MT    Count    TotalSize Class Name
    00152b18      230     40958584      Free
    Total 230 objects
    
  • Para determinar o espaço livre na geração 0, digite o seguinte comando para obter informações de consumo de memória pela geração:

    !eeheap -gc

    Este comando exibe saída similar à seguinte. A última linha mostra que o segmento efêmero.

    Heap 0 (0015ad08)
    generation 0 starts at 0x49521f8c
    generation 1 starts at 0x494d7f64
    generation 2 starts at 0x007f0038
    ephemeral segment allocation context: none
    segment  begin     allocated  size
    00178250 7a80d84c  7a82f1cc   0x00021980(137600)
    00161918 78c50e40  78c7056c   0x0001f72c(128812)
    007f0000 007f0038  047eed28   0x03ffecf0(67103984)
    3a120000 3a120038  3a3e84f8   0x002c84c0(2917568)
    46120000 46120038  49e05d04   0x03ce5ccc(63855820)
    
  • Calcule o espaço usado por geração 0:

    ? 49e05d04-0x49521f8c

    O resultado é o seguinte. Geração 0 é de aproximadamente 9 MB.

    Evaluate expression: 9321848 = 008e3d78
    
  • O comando a seguir Despeja o espaço livre dentro do intervalo de geração 0:

    !dumpheap -type Free -stat 0x49521f8c 49e05d04

    O resultado é o seguinte.

    ------------------------------
    Heap 0
    total 409 objects
    ------------------------------
    Heap 1
    total 0 objects
    ------------------------------
    Heap 2
    total 0 objects
    ------------------------------
    Heap 3
    total 0 objects
    ------------------------------
    total 409 objects
    Statistics:
          MT    Count TotalSize Class Name
    0015a498      409   7296540      Free
    Total 409 objects
    

    Esta saída mostra que a parte de geração 0 da heap usando o 9 MB de espaço para objetos e tem 7 MB livre. Essa análise mostra a extensão para a qual a geração 0 contribui para a fragmentação. Essa quantidade de uso da pilha deve ser descontada do valor total como a causa de fragmentação por objetos de longo prazo.

Para determinar o número de objetos fixados

  • No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando:

    !gchandles

    As estatísticas exibidas incluem o número de identificadores fixados, como mostra o exemplo a seguir.

    GC Handle Statistics:
    Strong Handles:      29
    Pinned Handles:      10
    

Para determinar o período de tempo em uma coleta de lixo

  • Examine o % Time in GC contador de desempenho de memória.

    O valor é calculado por meio de um intervalo de tempo de amostra. Porque os contadores são atualizados no final de cada coleta de lixo, a amostra atual terá o mesmo valor que o exemplo anterior, se não há coleções ocorreram durante o intervalo.

    Hora da coleta é obtida multiplicando-se o intervalo de tempo de amostra com o valor de porcentagem.

    Os dados a seguir mostram quatro intervalos de amostragem de dois segundos, para um estudo de 8 segundos. O Gen0, Gen1, e Gen2 as colunas mostram o número de coletas de lixo que ocorreu durante o intervalo para geração.

    Interval    Gen0    Gen1    Gen2    % Time in GC
           1       9       3       1              10
           2      10       3       1               1
           3      11       3       1               3
           4      11       3       1               3
    

    Essas informações não mostrará quando ocorreu a coleta de lixo, mas você pode determinar o número de coletas de lixo que ocorreram em um intervalo de tempo. Supondo que o pior caso, o décimo geração 0 lixo terminado no início do segundo intervalo e o décima primeiro geração 0 lixo terminado no final do quinto intervalo. O tempo entre o final do décimo e final a décima primeira coleta de lixo é de cerca de 2 segundos e o contador de desempenho mostra 3%, portanto, a duração da décima primeira coleta de lixo de geração 0 era (2 segundos * 3% = seja, 60 ms).

    Neste exemplo, existem 5 períodos.

    Interval    Gen0    Gen1    Gen2     % Time in GC
           1       9       3       1                3
           2      10       3       1                1
           3      11       4       2                1
           4      11       4       2                1
           5      11       4       2               20
    

    A segunda geração 2 coleta de lixo iniciado durante o intervalo de terceiro e terminaram no quinto intervalo. Supondo que o pior caso, a última coleta de lixo foi para uma coleção de geração 0 concluído no início do segundo intervalo e a coleta de lixo 2 de geração terminado no final do quinto intervalo. Portanto, o tempo entre o fim da coleta de lixo da geração 0 e o final da coleta de lixo da geração 2 é 4 segundos. Porque o % Time in GC contador é 20%, a quantidade máxima de tempo que a geração 2 coleta de lixo poderia levar é (4 segundos * 20% = 800ms).

  • Como alternativa, você pode determinar o comprimento de uma coleta de lixo usando eventos ETW de coleta de lixoe analisar as informações para determinar a duração de coleta de lixo.

    Por exemplo, os dados a seguir mostram uma seqüência de eventos que ocorreram durante uma coleta de lixo de não-simultâneo.

    Timestamp    Event name
    513052        GCSuspendEEBegin_V1
    513078        GCSuspendEEEnd
    513090        GCStart_V1
    517890        GCEnd_V1
    517894        GCHeapStats
    517897        GCRestartEEBegin
    517918        GCRestartEEEnd
    

    Suspender o thread gerenciado levou a 26us (GCSuspendEEEnd – GCSuspendEEBegin_V1).

    A coleta de lixo real levou 4.8ms (GCEnd_V1 – GCStart_V1).

    Continuando os threads gerenciados levou a 21us (GCRestartEEEnd – GCRestartEEBegin).

    A saída a seguir fornece um exemplo para coleta de lixo do plano de fundo e inclui o processo, thread e campos de evento. (Nem todos os dados é mostrado).

    timestamp(us)    event name            process    thread    event field
    42504385        GCSuspendEEBegin_V1    Test.exe    4372             1
    42504648        GCSuspendEEEnd         Test.exe    4372        
    42504816        GCStart_V1             Test.exe    4372        102019
    42504907        GCStart_V1             Test.exe    4372        102020
    42514170        GCEnd_V1               Test.exe    4372        
    42514204        GCHeapStats            Test.exe    4372        102020
    42832052        GCRestartEEBegin       Test.exe    4372        
    42832136        GCRestartEEEnd         Test.exe    4372        
    63685394        GCSuspendEEBegin_V1    Test.exe    4744             6
    63686347        GCSuspendEEEnd         Test.exe    4744        
    63784294        GCRestartEEBegin       Test.exe    4744        
    63784407        GCRestartEEEnd         Test.exe    4744        
    89931423        GCEnd_V1               Test.exe    4372        102019
    89931464        GCHeapStats            Test.exe    4372        
    

    O GCStart_V1 evento de 42504816 indica que uma coleta de lixo em segundo plano, como o último campo é 1. Isso se torna a coleta de lixo não. 102019.

    O GCStart evento ocorre porque há uma necessidade de uma coleta de lixo efêmera, antes de iniciar uma coleta de lixo do plano de fundo. Isso se torna a coleta de lixo não. 102020.

    Em 42514170, coleta de lixo não. 102020 terminar. Os threads gerenciados são reiniciados neste momento. Isso é concluído em thread 4372, que disparou essa coleta de lixo do plano de fundo.

    No thread 4744, uma suspensão ocorre. Esta é a única ocasião em que a coleta de lixo do plano de fundo deve suspender os threads gerenciados. Esta duração é de aproximadamente 99ms ((63784407-63685394)/1000).

    O GCEnd evento para a coleta de lixo em segundo plano está em 89931423. Isso significa que a coleta de lixo do plano de fundo lasted para sobre 47seconds ((89931423-42504816)/1000).

    Enquanto os threads gerenciados estão em execução, você pode ver qualquer número de coletas de lixo efêmeras ocorrendo.

Para determinar o que disparou uma coleta de lixo

  • No depurador WinDbg ou Visual Studio com a extensão de depurador SOS carregado, digite o seguinte comando para mostrar todos os threads com suas pilhas de chamadas:

    ~*kb

    Este comando exibe saída similar à seguinte.

    0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 
    0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4
    0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
    

    Se a coleta de lixo foi causada por uma notificação de pouca memória do sistema operacional, a pilha de chamadas é semelhante, exceto que o segmento é o finalizador. O thread do finalizador obtiver uma notificação assíncrona de pouca memória e o induz a coleta de lixo.

    Se a coleta de lixo foi causada pela alocação de memória, a pilha aparece como segue:

    0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration
    0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1
    0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18
    0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b
    0012f310 7a02ae4c mscorwks!Alloc+0x60
    0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd
    0012f424 300027f4 mscorwks!JIT_NewArr1+0x148
    000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c
    0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153
    

    Um auxiliar just-in-time (JIT_New*) finalmente chama GCHeap::GarbageCollectGeneration. Se você determinar que as coletas de lixo da geração 2 são causadas por alocações, você deve determinar quais objetos são coletados por uma coleta de lixo 2 de geração e como evitá-los. Ou seja, você deseja determinar a diferença entre o início e final de uma coleta de lixo 2 de geração e os objetos que causou a coleta de geração 2.

    Por exemplo, digite o seguinte comando no depurador para mostrar o início de uma coleção de geração 2:

    !dumpheap –stat

    Exemplo de saída (abreviado para mostrar os objetos que usam mais espaço):

    79124228    31857      9862328 System.Object[]
    035f0384    25668     11601936 Toolkit.TlkPosition
    00155f80    21248     12256296      Free
    79103b6c   297003     13068132 System.Threading.ReaderWriterLock
    7a747ad4   708732     14174640 System.Collections.Specialized.HybridDictionary
    7a747c78   786498     15729960 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700298     19608344 System.Collections.Specialized.ListDictionary
    035f0ee4    89192     38887712 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    7912c444    91616     71887080 System.Double[]
    791242ec    32451     82462728 System.Collections.Hashtable+bucket[]
    790fa3e0  2459154    112128436 System.String
    Total 6471774 objects
    

    Repita o comando no final da geração 2:

    !dumpheap –stat

    Exemplo de saída (abreviado para mostrar os objetos que usam mais espaço):

    79124228    26648      9314256 System.Object[]
    035f0384    25668     11601936 Toolkit.TlkPosition
    79103b6c   296770     13057880 System.Threading.ReaderWriterLock
    7a747ad4   708730     14174600 System.Collections.Specialized.HybridDictionary
    7a747c78   786497     15729940 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700298     19608344 System.Collections.Specialized.ListDictionary
    00155f80    13806     34007212      Free
    035f0ee4    89187     38885532 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    791242ec    32370     82359768 System.Collections.Hashtable+bucket[]
    790fa3e0  2440020    111341808 System.String
    Total 6417525 objects
    

    O double[] objetos desapareceram do final da saída, o que significa que elas foram coletadas. Esses objetos representam aproximadamente 70 MB. Os objetos restantes não alterou muito. Portanto, esses double[] objetos foram o motivo por que ocorreu a essa coleta de lixo da geração 2. Sua próxima etapa é saber porquê o double[] objetos estão lá e por que eles morreu. Você pode pedir que o desenvolvedor de código onde esses objetos vem, ou você pode usar o gcroot comando.

Para determinar se o alto uso da CPU é causado pela coleta de lixo

  • Correlacionar o % Time in GC valor de contador de desempenho de memória com o tempo de processo.

    Se o % Time in GC picos de valor ao mesmo tempo, como o tempo de processamento, a coleta de lixo está causando uma alta utilização da CPU. Caso contrário, o aplicativo para localizar onde está ocorrendo o alto uso de perfil.

Consulte também

Conceitos

Coleta de Lixo