O conselho geral é que você não deve ligar GC.Collect
do seu código, mas quais são as exceções a esta regra?
Só consigo pensar em alguns casos muito específicos em que pode fazer sentido forçar uma coleta de lixo.
Um exemplo que vem à mente é um serviço, que acorda a intervalos, realiza algumas tarefas e dorme por um longo tempo. Nesse caso, pode ser uma boa idéia forçar uma coleta para impedir que o processo que está prestes a ficar ocioso retenha mais memória do que o necessário.
Existem outros casos em que é aceitável ligar GC.Collect
?
c#
.net
garbage-collection
Brian Rasmussen
fonte
fonte
Respostas:
Se você tem um bom motivo para acreditar que um conjunto significativo de objetos - particularmente aqueles que você suspeita estar nas gerações 1 e 2 - agora são elegíveis para a coleta de lixo, e esse seria o momento apropriado para a coleta em termos do pequeno desempenho atingido .
Um bom exemplo disso é se você acabou de fechar um formulário grande. Você sabe que todos os controles da interface do usuário agora podem ser coletados como lixo e uma pausa muito curta à medida que o formulário é fechado provavelmente não será perceptível para o usuário.
ATUALIZAÇÃO 2.7.2018
A partir do .NET 4.5 - existe
GCLatencyMode.LowLatency
eGCLatencyMode.SustainedLowLatency
. Ao entrar e sair de um desses modos, é recomendável forçar um GC completoGC.Collect(2, GCCollectionMode.Forced)
.A partir do .NET 4.6 - existe o
GC.TryStartNoGCRegion
método (usado para definir o valor somente leituraGCLatencyMode.NoGCRegion
). Isso pode, por si só, executar uma coleta de lixo com bloqueio completo, na tentativa de liberar memória suficiente, mas, como estamos impedindo o GC por um período, eu diria que também é uma boa idéia executar o GC completo antes e depois.Fonte: Engenheiro da Microsoft, Ben Watson: Writing .NET High-Performance Code , 2nd Ed. 2018.
Vejo:
fonte
GC.Collect
você está dizendo, basicamente, o coletor de lixo que você sabe melhor do que isso para variar. Por que você não gosta do exemplo?GC.Collect()
solicitará que o GC realize uma coleção completa . Se você sabe que acabou de tornar elegíveis muitos objetos de vida anterior para a coleta de lixo e acredita que é menos provável que o usuário observe uma pequena pausa agora ou mais tarde, parece perfeitamente razoável pensar que agora é melhor hora de solicitar uma coleção do que deixá-la acontecer mais tarde.Eu uso
GC.Collect
somente ao escrever plataformas de teste de desempenho / criação de perfil bruto; ou seja, eu tenho dois (ou mais) blocos de código para testar - algo como:Assim que
TestA()
eTestB()
executado com o maior estado semelhante quanto possível - ou seja,TestB()
não se embebedar só porqueTestA
deixou muito perto do ponto de inflexão.Um exemplo clássico seria um exe de console simples (um
Main
método suficientemente ordenado para ser postado aqui, por exemplo), que mostra a diferença entre concatenação de strings em loop eStringBuilder
.Se eu precisar de algo preciso, então seriam dois testes completamente independentes - mas, com frequência, isso é suficiente se quisermos minimizar (ou normalizar) o GC durante os testes para ter uma ideia aproximada do comportamento.
Durante o código de produção? Ainda tenho que usá-lo ;-p
fonte
A melhor prática é não forçar uma coleta de lixo na maioria dos casos. (Todos os sistemas nos quais trabalhei que forçaram a coleta de lixo, tiveram problemas sublinhados que, se resolvidos, eliminariam a necessidade de forçar a coleta de lixo e aceleraram bastante o sistema.)
Existem alguns casos em que você sabe mais sobre o uso da memória e o coletor de lixo. É improvável que isso ocorra em um aplicativo multiusuário ou em um serviço que esteja respondendo a mais de uma solicitação por vez.
No entanto, em algum processamento de tipo de lote, você conhece mais do que o GC. Por exemplo, considere uma aplicação que.
Você poderá fazer um teste de caso (após cuidadoso) para forçar uma coleta de lixo completa depois de processar cada arquivo.
Outros casos é um serviço que acorda a cada poucos minutos para processar alguns itens e não mantém nenhum estado enquanto dorme . Forçar uma coleção completa antes de dormir pode valer a pena.
Eu preferiria ter uma API de coleta de lixo quando pudesse dar dicas sobre esse tipo de coisa sem ter que forçar um GC sozinho.
Veja também " Petiscos de desempenho da Rico Mariani "
fonte
Um caso é quando você está tentando código de teste de unidade que usa WeakReference .
fonte
Em grandes sistemas 24/7 ou 24/6 - sistemas que reagem a mensagens, solicitações de RPC ou que pesquisam um banco de dados ou processam continuamente - é útil ter uma maneira de identificar vazamentos de memória. Para isso, costumo adicionar um mecanismo ao aplicativo para suspender temporariamente qualquer processamento e executar a coleta de lixo completa. Isso coloca o sistema em um estado quieto em que a memória restante é legitimamente longa vida útil (caches, configuração, etc.) ou então é 'vazada' (objetos que não se espera ou que desejam que sejam enraizados, mas realmente são).
A existência desse mecanismo facilita muito o perfil do uso da memória, pois os relatórios não ficam embaçados com o ruído do processamento ativo.
Para ter certeza de obter todo o lixo, é necessário executar duas coleções:
Como a primeira coleção fará com que quaisquer objetos com finalizadores sejam finalizados (mas não na verdade colete esses objetos). O segundo GC coletará lixo esses objetos finalizados.
fonte
Você pode chamar GC.Collect () quando souber algo sobre a natureza do aplicativo que o coletor de lixo não conhece. É tentador pensar que, como autor, isso é muito provável. No entanto, a verdade é que o GC equivale a um sistema especialista bem escrito e testado, e é raro que você saiba algo sobre os caminhos de código de baixo nível que não conhece.
O melhor exemplo em que você pode ter informações extras é um aplicativo que alterna entre períodos de inatividade e períodos muito ocupados. Você deseja o melhor desempenho possível para os períodos de maior movimento e, portanto, deseja usar o tempo ocioso para fazer alguma limpeza.
No entanto, na maioria das vezes o GC é inteligente o suficiente para fazer isso de qualquer maneira.
fonte
Como uma solução de fragmentação de memória. Eu estava perdendo exceções de memória enquanto escrevia muitos dados em um fluxo de memória (lendo em um fluxo de rede). Os dados foram gravados em pedaços de 8K. Depois de atingir 128M, houve exceção, embora houvesse muita memória disponível (mas estava fragmentada). Ligar para GC.Collect () resolveu o problema. Eu era capaz de lidar com mais de 1G após a correção.
fonte
Dê uma olhada neste artigo de Rico Mariani. Ele fornece duas regras quando chamar GC.Collect (a regra 1 é: "Não"):
Quando ligar para GC.Collect ()
fonte
Uma instância em que é quase necessário chamar GC.Collect () é ao automatizar o Microsoft Office através da Interop. Os objetos COM do Office não gostam de ser liberados automaticamente e podem resultar em instâncias do produto do Office ocupando quantidades muito grandes de memória. Não tenho certeza se isso é um problema ou por design. Existem muitas postagens sobre esse tópico na Internet, então não vou entrar em muitos detalhes.
Ao programar usando a Interop, todos os objetos COM devem ser liberados manualmente, geralmente usando o Marshal.ReleseComObject (). Além disso, chamar a Garbage Collection manualmente pode ajudar a "limpar" um pouco. Chamar o código a seguir ao concluir com objetos de interoperabilidade parece ajudar bastante:
Na minha experiência pessoal, usar uma combinação de ReleaseComObject e chamar manualmente a coleta de lixo reduz bastante o uso de memória dos produtos do Office, especificamente o Excel.
fonte
Eu estava fazendo alguns testes de desempenho na matriz e na lista:
e cheguei
OutOfMemoryException
no método GetSomeNumbers_Enumerable_Range, a única solução alternativa é desalocar a memória:fonte
No seu exemplo, acho que chamar GC.Collect não é o problema, mas sim um problema de design.
Se você for acordar em intervalos, (horários definidos), seu programa deverá ser criado para uma única execução (executar a tarefa uma vez) e terminar. Em seguida, você configura o programa como uma tarefa agendada para execução nos intervalos agendados.
Dessa forma, você não precisa se preocupar em ligar para GC.Collect (o que você raramente deve fazer).
Dito isto, Rico Mariani tem um ótimo post sobre esse assunto, que pode ser encontrado aqui:
http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx
fonte
Um local útil para chamar GC.Collect () é em um teste de unidade quando você deseja verificar se não está criando um vazamento de memória (por exemplo, se você está fazendo algo com WeakReferences ou ConditionalWeakTable, código gerado dinamicamente, etc.).
Por exemplo, eu tenho alguns testes como:
Pode-se argumentar que o uso de WeakReferences é um problema por si só, mas parece que se você estiver criando um sistema que se baseia nesse comportamento, chamar GC.Collect () é uma boa maneira de verificar esse código.
fonte
A entrada de blog de Scott Holden sobre quando (e quando não) chamar GC.Collect é específica para o .NET Compact Framework , mas as regras geralmente se aplicam a todo o desenvolvimento gerenciado.
fonte
A resposta curta é: nunca!
fonte
fonte
Existem algumas situações em que é melhor prevenir do que remediar.
Aqui está uma situação.
É possível criar uma DLL não gerenciada em C # usando IL reescreve (porque há situações em que isso é necessário).
Agora, suponha, por exemplo, que a DLL crie uma matriz de bytes no nível da classe - porque muitas das funções exportadas precisam de acesso a elas. O que acontece quando a DLL é descarregada? O coletor de lixo é chamado automaticamente nesse ponto? Eu não sei, mas sendo uma DLL não gerenciada , é perfeitamente possível que o GC não seja chamado. E seria um grande problema se não fosse chamado. Quando a DLL é descarregada, também seria o coletor de lixo - quem será o responsável por coletar qualquer lixo possível e como eles o farão? Melhor empregar o coletor de lixo do C #. Tenha uma função de limpeza (disponível para o cliente DLL) em que as variáveis no nível da classe estão definidas como nulas e o coletor de lixo chamado.
Melhor prevenir do que remediar.
fonte
Ainda estou bastante inseguro sobre isso. Trabalho há 7 anos em um servidor de aplicativos. Nossas instalações maiores utilizam RAM de 24 GB. Suas chamadas altamente multithreaded e ALL para GC.Collect () tiveram problemas de desempenho realmente terríveis.
Muitos componentes de terceiros usaram o GC.Collect () quando acharam que era inteligente fazer isso agora. Portanto, um simples grupo de relatórios do Excel bloqueou o servidor de aplicativos para todos os threads várias vezes por minuto.
Tivemos que refatorar todos os componentes de terceiros para remover as chamadas GC.Collect () e tudo funcionou bem depois disso.
Mas também estou executando servidores no Win32 e aqui comecei a usar muito o GC.Collect () depois de obter uma OutOfMemoryException.
Mas também não tenho certeza sobre isso, porque muitas vezes notei que quando recebo um OOM em 32 bits e tento executar a mesma operação novamente, sem chamar GC.Collect (), funcionou bem.
Uma coisa que me pergunto é a própria exceção do OOM ... Se eu tivesse escrito o .Net Framework e não posso alocar um bloco de memória, usaria GC.Collect (), desfragmentar a memória (??), tente novamente , e se ainda não consigo encontrar um bloco de memória livre, lançaria a exceção OOM.
Ou, pelo menos, torne esse comportamento como opção configurável, devido às desvantagens do problema de desempenho com o GC.Collect.
Agora eu tenho muitos códigos como este no meu aplicativo para "resolver" o problema:
(Observe que o comportamento Thread.Sleep () é realmente um aplicativo específico para aplicativos, porque estamos executando um serviço de armazenamento em cache ORM, e o serviço leva algum tempo para liberar todos os objetos em cache, se a RAM exceder alguns valores predefinidos. alguns segundos na primeira vez e aumentou o tempo de espera a cada ocorrência de OOM.)
fonte
GC.Collect
. Como ele tem um efeito amplo no aplicativo, somente o aplicativo deve fazê-lo (se houver).If i would have written the .Net Framework, and i can't alloc a memory block, i would use GC.Collect(),
- Acho que eles já estão fazendo isso - vi indicações de que um dos gatilhos internos do GC é a falha na alocação de memória.Você deve evitar usar GC.Collect (), pois é muito caro. Aqui está um exemplo:
RESULTADO DO TESTE: USO DA CPU 12%
Quando você muda para isso:
RESULTADO DO TESTE: USO DA CPU 2-3%
fonte
Outro motivo é quando você tem um SerialPort aberto em uma porta USB COM e o dispositivo USB é desconectado. Como o SerialPort foi aberto, o recurso mantém uma referência à porta conectada anteriormente no registro do sistema. O registro do sistema conterá dados obsoletos , portanto a lista de portas disponíveis estará incorreta. Portanto, a porta deve estar fechada.
Chamar SerialPort.Close () na porta chama Dispose () no objeto, mas permanece na memória até a coleta de lixo ser executada, fazendo com que o registro permaneça obsoleto até que o coletor de lixo decida liberar o recurso.
De https://stackoverflow.com/a/58810699/8685342 :
fonte
Isso não é relevante para a questão, mas, para as transformações XSLT no .NET (XSLCompiledTranform), talvez você não tenha escolha. Outro candidato é o controle MSHTML.
fonte
Se você estiver usando uma versão do .net menor que 4.5, a coleta manual poderá ser inevitável (especialmente se você estiver lidando com muitos 'objetos grandes').
este link descreve o motivo:
https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/
fonte
um bom motivo para ligar para o GC é em pequenos computadores ARM com pouca memória, como o Raspberry PI (executando com mono). Se fragmentos de memória não alocados usarem muita RAM do sistema, o sistema operacional Linux poderá ficar instável. Eu tenho um aplicativo em que preciso chamar o GC a cada segundo (!) Para se livrar dos problemas de estouro de memória.
Outra boa solução é descartar objetos quando não forem mais necessários. Infelizmente, isso não é tão fácil em muitos casos.
fonte
Como existem objetos pequenos (SOH) e objetos grandes (LOH)
Podemos chamar GC.Collect () para limpar o objeto de des-referência no SOP e mover o objeto ativo para a próxima geração.
No .net4.5, também podemos compactar o LOH usando largeobjectheapcompactionmode
fonte
Se você estiver criando muitos
System.Drawing.Bitmap
objetos novos , o Garbage Collector não os limpará. Eventualmente, o GDI + pensará que você está ficando sem memória e lançará uma exceção "O parâmetro não é válido". Ligar deGC.Collect()
vez em quando (não com muita frequência!) Parece resolver esse problema.fonte