Quando é aceitável chamar GC.A cobrar?

o conselho geral é que você não deve ligar {[[0]} do seu código, mas quais são as exceções a esta regra?

Só consigo pensar em alguns casos específicos em que faz sentido forçar uma recolha de lixo.

Um exemplo que me vem à mente é um serviço, que acorda em intervalos, executa alguma tarefa, e depois dorme durante muito tempo. Neste caso, pode ser uma boa ideia forçar uma recolha para evitar o processo de agarrando-se a mais memória do que o necessário.

Existem outros casos em que é aceitável chamar GC.Collect?

Author: Brian Rasmussen, 2009-01-25

24 answers

Se você tem boas razões para acreditar que um conjunto significativo de objetos - particularmente aqueles que você suspeita estar nas gerações 1 e 2 - são agora elegíveis para a coleta de lixo, e que agora seria um momento apropriado para coletar em termos do pequeno sucesso de desempenho.

Um bom exemplo disso é se você acabou de fechar uma forma grande. Você sabe que todos os controles UI podem agora ser coletados lixo, e uma pausa muito curta como o formulário está fechado provavelmente não será perceptível para o usuario.

Actualização 2.7.2018

A partir de. NET 4.5-há GCLatencyMode.LowLatency e GCLatencyMode.SustainedLowLatency. Ao entrar e sair de qualquer um destes modos, recomenda-se que force um GC completo com GC.Collect(2, GCCollectionMode.Forced).

A partir de. NET 4.6-existe o método GC.TryStartNoGCRegion (Usado para definir o valor apenas para leitura GCLatencyMode.NoGCRegion). Esta lata em si, executar uma coleção de lixo de bloqueio completo em uma tentativa de libertar memória suficiente, mas dado que estamos desautorizando GC por um período, eu diria que é também uma boa idéia para executar GC completo antes e apos.

Fonte: Microsoft engineer Ben Watson: Writing High-Performance. net Code , 2nd Ed. 2018.

Ver:

 156
Author: Jon Skeet, 2018-07-02 20:13:45

Só uso {[1] } quando escrevo plataformas de teste de desempenho/perfil em bruto; ou seja, tenho dois (ou mais) blocos de código para testar-algo como:

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

Para que TestA() e TestB() corram com o estado mais semelhante possível - isto é, TestB() não se embebedem só porque TestA o deixaram muito perto do ponto de viragem.

Um exemplo clássico seria um simples consola exe( um método de ordenação Main - o suficiente para ser publicado aqui, por exemplo), que mostra a diferença entre cadeia de caracteres em looped concatenação e StringBuilder.

Se eu precisar de algo preciso, então esta seria dois completamente independentes de testes, mas muitas vezes isso é suficiente se nós apenas queremos minimizar (ou normalizar) o GC durante os testes para obter uma sensação áspera para o comportamento.

Durante o código de produção? Ainda não o usei; - p

 52
Author: Marc Gravell, 2017-03-20 18:01:17

A melhor prática é não forçar uma coleta de lixo na maioria dos casos.(Todos os sistemas em que trabalhei que forçaram coleções de lixo, tiveram problemas que se resolvidos teriam removido a necessidade de forçar a coleta de lixo, e aceleraram muito o sistema.)

Há um poucos casos Quando você Sabe mais sobre o uso da memória do que o coletor de lixo sabe. É pouco provável que isso seja verdade em uma aplicação multi-usuário, ou um serviço que está a responder a mais do que um pedido de cada vez.

No entanto, em algum processamento do tipo de lote {[3] } você sabe mais do que o GC. Por exemplo, considere uma aplicação que.

  • é dada uma lista de nomes de ficheiros na linha de comandos
  • processa um único ficheiro e depois escreve o resultado para um ficheiro de resultados.
  • ao processar o ficheiro, cria um monte de objectos interligados que não podem ser recolhidos até que o processamento do ficheiro esteja completo (por exemplo, uma análise comparativa). árvore)
  • não mantém o estado de correspondência entre os ficheiros que processou .

Você Pode ser capaz de fazer um caso (depois de cuidadoso) testando que você deve forçar uma coleção completa de lixo depois de ter processado cada arquivo.

Outro caso é um serviço que acorda a cada poucos minutos para processar alguns itens, e {[[2]}não mantém nenhum Estado enquanto está dormindo . Em seguida, forçando uma coleção completa pouco antes de ir dormir Pode ser util.
A única vez que consideraria forçar uma coleção é quando eu sei que muito do objecto foi criado recentemente e muito poucos objetos são atualmente relacionar.
Preferia ter uma API de recolha de lixo quando podia dar-lhe pistas sobre este tipo de coisas sem ter de forçar um GC.

Ver também "As tidbits de desempenho de Rico Mariani"

 29
Author: Ian Ringrose, 2009-09-24 15:47:52

Um caso é quando se está a tentar usar o código de teste unitário que usa O WeakReference.

 19
Author: Brian, 2009-01-25 20:08:21

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 processo continuamente -- é útil ter uma maneira de identificar vazamentos de memória. Para isso, eu tendem a adicionar um mecanismo à aplicação para suspender temporariamente qualquer processamento e, em seguida, realizar a coleta completa de lixo. Isto coloca o sistema em um estado quiescente, onde a memória restante é ou a memória legitimamente longa (caches, configuração, & C.) ou então é 'vazada' (objetos que não são esperados ou desejados para ser enraizado, mas na verdade são).

Ter este mecanismo torna muito mais fácil a utilização da memória de perfil, uma vez que os relatórios não serão toldados com o ruído do processamento activo.

Para ter a certeza de que consegue todo o lixo, precisa de realizar duas colecções:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Como a primeira coleção fará com que todos os objetos com finalizadores sejam finalizados (mas não na verdade lixo coletar esses objetos). O segundo GC vai recolher lixo estes finalizados objecto.

 11
Author: Paul Ruane, 2011-06-27 12:05:12
Podes ligar à GC.Coletar () quando você sabe algo sobre a natureza do aplicativo o coletor de lixo não. é tentador pensar que, como o autor, isso é muito provável. No entanto, a verdade é que o GC é um sistema de especialistas bastante bem escrito e testado, e é raro que você saiba algo sobre os caminhos de código de baixo nível que ele não sabe.

O melhor exemplo que consigo pensar de onde poderá ter alguma informação extra é uma aplicação que circula entre períodos de inactividade e muito ocupada periodo. Você quer o melhor desempenho possível para os períodos ocupados e, portanto, quer usar o tempo ocioso para fazer alguma limpeza.

No entanto, a maior parte das vezes o GC é esperto o suficiente para fazer isto.
 10
Author: Joel Coehoorn, 2018-10-16 20:33:47
Como uma solução de fragmentação de memória. Eu estava saindo de exceções de memória ao escrever um monte de dados em um fluxo de memória (leitura de um fluxo de rede). Os dados foram escritos em pedaços de 8K. Depois de atingir os 128M, houve exceção, embora houvesse muita memória disponível (mas ela estava fragmentada). Chamando GC.Coletar () resolveu o problema. Consegui lidar com mais de 1G depois da dose.
 8
Author: Ethos, 2013-10-17 23:06:55
Veja este artigo de Rico Mariani. Ele dá duas regras quando ligar para a GC.Recolher (a regra 1 é:"não"):

Quando ligar para a GC.Recolher()

 7
Author: M4N, 2009-01-25 20:08:05

Estava a fazer alguns testes de desempenho no array e na lista:

private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
    var lstNumbers = new List<int>();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List<int>\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

E eu tenho {[2] } no método GetSomeNumbers_Enumerable_Range a única solução é desligar a memória por:

numbers = null;
GC.Collect();
 6
Author: Daniel B, 2017-09-08 18:30:31
Um caso em que é quase necessário chamar a GC.Colet () é quando automatiza o Microsoft Office através da Interop. Os objetos COM para o escritório não gostam de liberar automaticamente e pode resultar nas instâncias do produto de escritório que absorvem grandes quantidades de memória. Não tenho a certeza se isto é um problema ou por desígnio. Há muitos posts sobre este tópico em torno da internet para que eu não vou entrar em muitos detalhes.

Ao programar usando o Interop, cada COM o objeto deve ser liberado manualmente, geralmente através do uso de Marshal.ReleseComObject(). Além disso, chamar a coleta de lixo manualmente pode ajudar a "limpar" um pouco. Chamar o seguinte código quando você terminar com objetos Interop parece ajudar bastante:

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

Na minha experiência pessoal, usar uma combinação de ReleaseComObject e chamar manualmente a recolha de lixo reduz grandemente o uso de memória de produtos de escritório, especificamente o Excel.

 5
Author: garthhh, 2017-07-11 17:10:43
No seu exemplo, acho que chamar GC.Coletar não é o problema, mas sim há um problema de design.

Se você vai acordar em intervalos, (definir horas) então o seu programa deve ser criado para uma única execução (executar a tarefa uma vez) e, em seguida, terminar. Em seguida, você configura o programa como uma tarefa agendada para executar nos intervalos agendados.

Assim, não tens de te preocupar em ligar à GC.Raramente Se alguma vez, já tarefa). Dito isto, Rico Mariani tem um grande post no blog sobre este assunto, que pode ser encontrado aqui:

Http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx

 4
Author: casperOne, 2009-01-25 20:08:52
Há situações em que é melhor prevenir do que remediar. Eis uma situação.

É possível autor de umnão gerido DLL em C# usando a reescrita IL (porque existem situações em que isso é necessário).

Suponha agora, por exemplo, que o DLL cria um array de bytes ao nível da classe - porque muitas das funções exportadas precisam de acesso a tal. O que acontece quando o DLL é descarregado? É o coletor de lixo chamado automaticamente em esse ponto? Não sei, mas sendo um não gerido, é possível que o GC não seja chamado. E seria um grande problema se não fosse chamado. Quando o DLL é descarregado assim também seria o coletor de lixo - então quem vai ser responsável pela coleta de qualquer lixo possível e como eles fariam isso? É melhor contratar o cobrador de lixo da c#. Ter uma função de Limpeza (disponível para o cliente DLL) onde as variáveis de nível de classe são definidas como nulas e o coletor de lixo conhecer.

Mais vale prevenir do que remediar.
 3
Author: Carl Looper, 2011-08-18 20:14:32

Um lugar útil para chamar GC.O Collect () está num teste de unidade quando quer verificar que não está a criar uma fuga de memória (por exemplo, se está a fazer alguma coisa com referências fracas ou um código Condicionalweaktable, dinamicamente gerado, etc.).

Por exemplo, Tenho alguns testes como:
WeakReference w = CodeThatShouldNotMemoryLeak();
Assert.IsTrue(w.IsAlive);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsFalse(w.IsAlive);
Pode-se argumentar que o uso de referências fracas é um problema em si mesmo, mas parece que se você está criando um sistema que depende de tal comportamento, Então chamando GC.Recolher() é um boa maneira de verificar esse código.
 3
Author: ChaseMedallion, 2013-07-03 20:33:58

A entrada no blog de Scott Holden sobre quando (e quando não) ligar para a GC.O Collect é específico do . Net Compact Framework , mas as regras aplicam-se geralmente a todo o desenvolvimento gerido.

 3
Author: ctacke, 2019-03-07 20:57:51
A resposta curta é: nunca!
 2
Author: BankZ, 2009-01-25 20:19:45
using(var stream = new MemoryStream())
{
   bitmap.Save(stream, ImageFormat.Png);
   techObject.Last().Image = Image.FromStream(stream);
   bitmap.Dispose();

   // Without this code, I had an OutOfMemory exception.
   GC.Collect();
   GC.WaitForPendingFinalizers();
   //
}
 2
Author: Denis, 2011-08-05 05:10:42
Ainda não tenho a certeza. Trabalho há 7 anos num servidor de aplicações. As nossas instalações maiores utilizam Ram de 24 GB. Sua alta Multithreaded, e todas as chamadas para GC.Coletar() teve problemas de desempenho realmente terríveis.

Muitos componentes de terceiros usaram GC.Quando eles pensaram que era inteligente fazer isto agora. Então um simples grupo de relatórios do Excel bloqueou o servidor de aplicativos para todos os threads várias vezes por minuto.

Tivemos de refacotar tudo. os Componentes da terceira parte, a fim de remover o GC.Chamadas a cobrar (), e tudo funcionou bem depois de fazer isso. Mas também estou a correr servidores no Win32, e aqui comecei a usar muito o GC.Recolha() após ter recebido uma derrogação excessiva. [[2]} mas eu também estou bastante inseguro sobre isso, porque eu muitas vezes notei, quando eu recebo um OOM em 32 bits, e eu tento executar a mesma operação novamente, sem chamar GC.Coletar(), funcionou muito bem. Uma coisa que me pergunto é o OOM. Excepção em si... Se eu tivesse escrito o Framework. Net, e não pudesse alocar um bloco de memória, eu usaria GC.Collect (), defrag memory (??), try again, and if i still can find a free memory block, then I would throw the OOM-Exception.

Ou pelo menos fazer este comportamento como opção configurável, devido às desvantagens do problema de desempenho com o GC.Recolher.

Agora tenho muitos códigos como este no meu aplicativo para "resolver"o problema:
public static TResult ExecuteOOMAware<T1, T2, TResult>(Func<T1,T2 ,TResult> func, T1 a1, T2 a2)
{

    int oomCounter = 0;
    int maxOOMRetries = 10;
    do
    {
        try
        {
            return func(a1, a2);
        }
        catch (OutOfMemoryException)
        {
            oomCounter++;
            if (maxOOMRetries > 10)
            {
                throw;
            }
            else
            {
                Log.Info("OutOfMemory-Exception caught, Trying to fix. Counter: " + oomCounter.ToString());
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(oomCounter * 10));
                GC.Collect();
            }
        }
    } while (oomCounter < maxOOMRetries);

    // never gets hitted.
    return default(TResult);
}

(Note que o fio.Sono() comportamento é um comportamento apecífico, porque estamos executando um serviço de cache ORM, e o Serviço leva algum tempo para liberar todos os objetos Cache, se a RAM exceder alguns valores pré-definidos. por isso, espera alguns segundos da primeira vez, e aumentou o tempo de espera a cada ocorrência de OOM.)

 2
Author: Thomas Haller, 2013-10-17 23:27:32

Deve tentar evitar o uso de GC.Recolher() desde que é muito caro. Aqui está um exemplo:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet.Remove(RecordSet[0]);

        }
        GC.Collect(); // AVOID
    }

RESULTADO DO TESTE: UTILIZAÇÃO DO CPU 12%

Quando Mudar para este:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet[0].Dispose(); //  Bitmap destroyed!
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet[0].Dispose(); //  Bitmap destroyed!
            RecordSet.Remove(RecordSet[0]);

        }
        //GC.Collect();
    }

RESULTADO DO TESTE: UTILIZAÇÃO DO CPU 2-3%

 2
Author: mtekeli, 2014-02-22 22:58:30
Outra razão é quando se tem uma porta de série aberta numa porta USB, e depois o dispositivo USB é desligado. Como o SerialPort foi aberto, o recurso contém uma referência ao porto anteriormente conectado no registro do sistema. O registo do sistema irá então conter dados obsoletos, de modo que a lista de portos disponíveis estará errada. Portanto, o porto deve ser fechado. Chamando SerialPort.Close() on the port calls Dispose () on the object, but it remains in memory até que a coleta de lixo realmente funcione, fazendo com que o registro permaneça parado até que o coletor de lixo decida liberar o recurso.

De https://stackoverflow.com/a/58810699/8685342:

try
{
    if (port != null)
        port.Close(); //this will throw an exception if the port was unplugged
}
catch (Exception ex) //of type 'System.IO.IOException'
{
    System.GC.Collect();
    System.GC.WaitForPendingFinalizers();
}

port = null;
 1
Author: Jay Tennant, 2019-11-12 01:42:01

Isto não é relevante para a questão, mas para o XSLT transforma-se em.Net (XSLCompiledTranform) então você pode não ter escolha. Outro candidato é o controle MSHTML.

 0
Author: Chris S, 2009-01-25 20:36:08

Se estiver a usar uma versão do. Net inferior a 4.5, a colecção manual pode ser inevitável (especialmente se estiver a lidar com muitos 'objectos grandes').

Este link descreve o porquê:

Https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/

 0
Author: fusi, 2016-08-16 10:46:12
Uma boa razão para chamar GC é em computadores de braço pequeno com pouca memória, como o Raspberry PI (executando com mono). Se fragmentos de memória não alocados usam muito da RAM do sistema, então o sistema operacional Linux pode ficar instável. Tenho uma candidatura em que tenho de ligar ao GC a cada segundo.) para se livrar de problemas de excesso de memória. Outra boa solução é eliminar objectos quando já não são necessários. Infelizmente, isso não é tão fácil em muitos casos.
 0
Author: harry4516, 2017-01-08 14:13:16

Uma vez que existem pequenos objectos heap(SOH) e large object heap (LOH)

Podemos ligar para a GC.Coletar () para limpar o objeto de referência no SOP, e mover o objeto vivido para a próxima geração.

In .net4. 5, we can also compact LOH by using largeobjecttheapcompactionmode

 0
Author: Max CHien, 2017-12-28 02:06:25
Se você está criando muitos objetos Novos, o coletor de lixo não os limpa. Eventualmente o GDI+ vai pensar que você está ficando sem memória e vai lançar uma exceção "o parâmetro não é válido". Chamando {[1] } de vez em quando (não muitas vezes!) parece resolver esta questão.
 0
Author: Nacht, 2020-05-27 02:17:13