Garbage Collector no .NET: Não utilize o GC.Collect a menos que saiba o que esteja fazendo
Em aplicações line of business dificilmente você precisará da utilização desse recurso. Portanto, minha recomendação é que não utilize o GC.Collect a menos que saiba o que esteja fazendo. Em outras palavras, o Garbage Collector no .NET é inteligente o suficiente para, sem customizações, funcionar muito bem e desempenhar seu trabalho com muita eficiência.
Sendo assim, não conhecer seu funcionamento, habilita programadores a cometer erros que podem comprometer a performance de aplicações .NET. O uso indiscriminado deste recurso, com propósito de liberar memória alocada, é um desses erros mais encontrados em diversas aplicações.
O que é o GC.Collect?
O GC.Collect “força” a coleta imediata do lixo pelo Garbage Collector com o propósito de liberar memória, indicando ao runtime para efetuar uma coleta assim que possível.
Como utilizar o GC.Collect?
Com uma sobrecarga no método, você pode determinar a geração do GC a ser coletada. Na prática, será forçada a coleta de lixo partindo da geração 0 até a geração especificada.
Teremos a coleta de lixo sendo feita em todas as gerações, uma vez que foi especificado a geração 2. Quando mencionamos a geração 0 no método as demais gerações não serão acionadas. Isso reforça o conceito de que a coleta acontece da geração 0 até a geração especificada.
1 |
GC.Collect(2); |
Assim sendo, podemos utilizar mais dois parâmetros, sendo um é GCCollectionMode e outro blocking. Quando utilizados atendem a melhor estratégia da coleta, garantindo, inclusive, performance.
A maneira que ocorrerá a coleta é determinada pelo GCCollectionMode
O GCCollectionMode indica se a coleta será forçada ou otimizada. A forçada, determinará que a coleta ocorra imediatamente, enquanto a otimizada permite ao garbage collector qual será o momento ideal para a coleta.
1 |
public enum GCCollectionMode |
Default | 0 | A configuração padrão dessa enumeração, que atualmente é Forced |
Forced | 1 | Força a passagem do garbage collector imediatamente |
Optimized | 2 | Permite que o garbage collector determine o momento ideal para realizar a coleta |
A forma de bloqueio do GC é feita pelo parâmetro blocking
Continuando, o parâmetro blocking, quando true, permite a coleta com bloqueio, enquanto false executa a coleta em segundo plano e sempre que possível.
mode | blocking como true | blocking como false |
---|---|---|
Forced ou Default | Uma coleção de bloqueio é executada assim que possível. Se uma coleção em segundo plano estiver em andamento e generation for 0 ou 1, o Collect(Int32, GCCollectionMode, Boolean) método disparará imediatamente uma coleção de bloqueio e retornará quando a coleção for concluída. Se uma coleção em segundo plano estiver em andamento e generation for 2, o método aguardará até que a coleção em segundo plano seja concluída, disparará uma coleção de geração 2 de bloqueio e retornará. | Uma coleta é executada assim que possível. O método Collect(Int32, GCCollectionMode, Boolean) solicita uma coleta em segundo plano, mas isso não é garantido; dependendo das circunstâncias, uma coleta de bloqueio ainda pode ser executada. Se uma coleta em segundo plano já estiver em andamento, o método retornará imediatamente. |
Optimized | Uma coleta de bloqueio pode ser executada, dependendo do estado do coletor de lixo e do parâmetro generation. O coletor de lixo tenta fornecer um desempenho ideal. | Uma coleta pode ser executada, dependendo do estado do coletor de lixo. O método Collect(Int32, GCCollectionMode, Boolean) solicita uma coleta em segundo plano, mas isso não é garantido; dependendo das circunstâncias, uma coleta de bloqueio ainda pode ser executada. O coletor de lixo tenta fornecer um desempenho ideal. Se uma coleta em segundo plano já estiver em andamento, o método retornará imediatamente. |
Como saber se o GC.Collect está sendo utilizado?
A princípio, toda ação do Garbage Collector pode ser monitorada. Seja por um APM, bem como Datadog ou Prometheus em conjunto como prometheus-DotnetRuntime. Do mesmo modo você pode utilizar o Windows Performance Monitor ou pelo PerfView. Seja como for, é possível monitorar e entender se o GC.Collect está causando algum prejuízo para sua aplicação.
Utilize o contador #Induced GC para descobrir quantas coleções originadas pelo GC.Collect são feitas em sua aplicação. Os acionamentos poderão ser percebidos na quantidade de coletas em g0, g1 e g2 além do percentual do tempo no garbage collector.
Utilize as métricas certas para problemas específicos
contador | descrição |
---|---|
# Induced GC | Exibe o número máximo de vezes que uma coleta de lixo foi executada devido a uma chamada explícita para GC.Collect. |
# Gen 0 Collections | Número de coleções em g0 |
# Gen 1 Collections | Número de coleções em g1 |
# Gen 2 Collections | Número de coleções em g2 |
% Time in GC | Porcentagem de tempo gasto no GC desde o final do último GC. Métrica extremamente relevante, quanto maior o tempo, pior para performance. |
Conclusão
Por fim, você não precisa utilizar o GC.Collect a menos que saiba o que está fazendo. Em projetos line of business, até hoje, não vi casos que necessitassem da chamada induzida do garbage collector. O uso descuidado, poderá colocar sua aplicação em risco, com perda considerável de performance.
Portanto, não utilize o GC.Collect a menos que saiba o que esteja fazendo.