Tiago Tartari
Performance

Não utilize GC.Collect a menos que saiba o que esteja fazendo

publicado em 08 de novembro de 2022
Resumo
Se você tem problemas de performance em suas aplicações .NET, certifique-se não estar utilizando o GC.Collect para "liberar" espaço em memória. Problemas de performance em aplicações .NET geralmente são ocasionadas por descuidos que facilmente seriam contornados com um entendimento claro e prático de aspectos como Garbage Collector.

Conteúdo

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.

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.

public enum GCCollectionMode
Default0A configuração padrão dessa enumeração, que atualmente é Forced
Forced1Força a passagem do garbage collector imediatamente
Optimized2Permite 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.

modeblocking como trueblocking 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.
OptimizedUma 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.
Fonte: Microsoft

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

contadordescrição
# Induced GCExibe 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 CollectionsNúmero de coleções em g0
# Gen 1 CollectionsNúmero de coleções em g1
# Gen 2 CollectionsNúmero de coleções em g2
% Time in GCPorcentagem 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.

Gostou? Me ajude a impactar outras pessoas compartilhando esse post.
conheça mais sobre: .net, garbage collector
Tiago Tartari
Tiago Tartari

Ajudo executivos, times de negócios e especialistas técnicos a resolver problemas complexos utilizando a tecnologia como meio para potencializar resultados.