O que há de errado com o uso do GC.Collect ()?

Embora eu entenda as sérias implicações de brincar com essa function (ou pelo menos é o que eu acho), eu não consigo entender por que isso está se tornando uma daquelas coisas que programadores respeitáveis ​​jamais usariam, mesmo aqueles que nem sequer sabem Para que isso serve.

Digamos que eu esteja desenvolvendo um aplicativo em que o uso da memory varia muito, dependendo do que o usuário está fazendo. O ciclo de vida da aplicação pode ser dividido em duas etapas principais: edição e processamento em tempo real. Durante o estágio de edição, suponha que bilhões ou até trilhões de objects sejam criados; alguns deles pequenos e outros não, alguns podem ter finalizadores e outros não, e supor que sua vida varia de poucos milissegundos a longas horas. Em seguida, o usuário decide mudar para o estágio em tempo real. Neste ponto, suponha que o desempenho desempenhe um papel fundamental e que a menor alteração no stream do programa possa trazer conseqüências catastróficas. A criação de objects é então reduzida ao mínimo possível usando pools de objects e o tal, mas, em seguida, o GC entra inesperadamente e joga tudo fora, e alguém morre.

A questão: Neste caso, não seria sensato chamar o GC.Collect () antes de entrar no segundo estágio?

Afinal, esses dois estágios nunca se sobrepõem no tempo um com o outro e toda a otimização e estatística que o GC poderia ter reunido seriam de pouca utilidade aqui …

Nota: Como alguns de vocês apontaram, o .NET pode não ser a melhor plataforma para um aplicativo como este, mas isso está além do escopo desta questão. A intenção é esclarecer se uma chamada GC.Collect () pode melhorar o comportamento / desempenho geral de um aplicativo ou não. Nós todos concordamos que as circunstâncias sob as quais você faria tal coisa são extremamente raras, mas, novamente, o GC tenta adivinhar e faz isso perfeitamente na maioria das vezes, mas ainda é sobre adivinhação.

Obrigado.

Do Blog do Rico …

Regra 1

Não faça

Esta é realmente a regra mais importante. É justo dizer que a maioria dos usos do GC.Collect () é uma má idéia e eu entrei nisso com algum detalhe na publicação original, então não vou repetir tudo isso aqui. Então vamos passar para …

Regra nº 2

Considere chamar GC.Collect () se algum evento não recorrente tiver acabado de acontecer e esse evento provavelmente causou a morte de muitos objects antigos.

Um exemplo clássico disso é se você está escrevendo um aplicativo cliente e exibe um formulário muito grande e complicado que contém muitos dados associados a ele. Seu usuário acabou de interagir com este formulário potencialmente criando alguns objects grandes … coisas como documentos XML ou um DataSet grande ou dois. Quando o formulário fecha, esses objects estão mortos e então GC.Collect () recuperará a memory associada a eles …

Assim, parece que esta situação pode estar na Regra 2, você sabe que há um momento no tempo em que muitos objects antigos morreram e não é recorrente. No entanto, não esqueça as palavras de despedida de Rico.

Regra nº 1 deve triunfar a Regra nº 2 sem evidência forte.

Meça, meça, meça.

Se você chamar GC.Collect () no código de produção, estará essencialmente declarando que sabe mais do que os autores do GC. Talvez seja o caso. No entanto, geralmente não é e, portanto, fortemente desencorajado.

Então, como quando você está usando objects COM como o MS Word ou o MS Excel de .net? Sem chamar GC.Collect depois de liberar os objects COM, descobrimos que as instâncias do aplicativo Word ou Excel ainda existem.

Na verdade, o código que usamos é:

 Utils.ReleaseCOMObject(objExcel) ' Call the Garbage Collector twice. The GC needs to be called twice in order to get the ' Finalizers called - the first time in, it simply makes a list of what is to be finalized, ' the second time in, it actually does the finalizing. Only then will the object do its ' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, ' but one that may be necessary when automating Excel because it is the only way to ' release all the Excel COM objects referenced indirectly. ' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5 ' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109 GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() 

Então, isso seria um uso incorreto do coletor de lixo? Se sim, como vamos fazer com que os objects Interop morram? Além disso, se não é para ser usado assim, por que o método Collect da GC é Public ?

Bem, o GC é uma daquelas coisas com as quais tenho uma relação de amor / ódio. Nós quebramos isso no passado através do VistaDB e escrevemos sobre isso. Eles consertaram, mas leva um bom tempo para conseguir consertos deles em coisas como essa.

O GC é complexo, e uma abordagem única para todos os tamanhos é muito, muito difícil de realizar em algo tão grande. MS fez um bom trabalho, mas é possível enganar o GC às vezes.

Em geral, você não deve adicionar um Collect menos que você saiba que você acabou de despejar uma tonelada de memory e ele irá para uma crise no meio da vida se o GC não conseguir limpá-lo agora.

Você pode estragar a máquina inteira com uma série de declarações ruins GC.Collect . A necessidade de uma instrução de coleta quase sempre aponta para um erro subjacente maior. O memory leaks geralmente tem a ver com referências e falta de compreensão de como elas funcionam. Ou usar o IDisposable em objects que não precisam dele e colocar uma carga muito maior no GC.

Observe atentamente o% de tempo gasto no GC através dos contadores de desempenho do sistema. Se você vir seu aplicativo usando 20% ou mais de seu tempo no GC, você terá sérios problemas de gerenciamento de objects (ou um padrão de uso anormal). Você quer sempre minimizar o tempo que o GC gasta, porque isso acelera todo o aplicativo.

Também é importante observar que o GC é diferente em servidores que em estações de trabalho. Eu vi um número de pequenos difíceis de rastrear problemas com pessoas não testando os dois (ou nem mesmo cientes de que são dois deles).

E só para estar o mais completo possível na minha resposta, você também deve testar com o Mono se também estiver segmentando essa plataforma. Como é uma implementação totalmente diferente, pode ter problemas totalmente diferentes que a implementação do MS.

Existem situações em que é útil, mas em geral deve ser evitado. Você pode compará-lo com GOTO ou andar de motoqueiro: você faz isso quando precisa, mas não conta a seus amigos sobre isso.

De minha experiência, nunca foi aconselhável fazer uma chamada para GC.Collect () no código de produção. Na debugging, sim, tem suas vantagens para ajudar a esclarecer possíveis vazamentos de memory. Eu acho que a minha razão fundamental é que o GC foi escrito e otimizado por programadores muito mais inteligentes do que eu, e se eu chegar a um ponto que eu sinto que preciso chamar GC.Collect () é um indício de que eu saí do caminho algum lugar. Na sua situação, não parece que você realmente tenha problemas de memory, apenas que você está preocupado com a instabilidade que a coleção trará ao seu processo. Vendo que não vai limpar objects ainda em uso, e que ele se adapta muito rapidamente às demandas crescentes e redutoras, eu acho que você não terá que se preocupar com isso.

Bem, obviamente você não deve escrever código com requisitos em tempo real em idiomas com garbage collection não em tempo real.

Em um caso com estágios bem definidos, não há problema em acionar o coletor de lixo. Mas este caso é extremamente raro. O problema é que muitos desenvolvedores tentarão usar isso em problemas de paper-over em um estilo de culto de carga, e adicioná-lo indiscriminadamente causará problemas de desempenho.

Uma das maiores razões para chamar GC.Collect () é quando você acaba de realizar um evento significativo que cria muito lixo, como o que você descreve. Chamar GC.Collect () pode ser uma boa ideia aqui; caso contrário, o GC pode não entender que foi um evento ‘único’.

Claro, você deve fazer um perfil e ver por si mesmo.

Chamar GC.Collect () força o CLR a fazer um stack walk para ver se cada object pode ser liberado verificando referências. Isso afetará a escalabilidade se o número de objects for alto e também se sabe que ele aciona a garbage collection com muita freqüência. Confie no CLR e deixe o coletor de lixo executar a si próprio quando apropriado.

Em .net, o tempo necessário para executar uma garbage collection é muito mais relacionado à quantidade de coisas que não são lixo do que à quantidade de coisas que são. De fato, a menos que um object substitua Finalize (seja explicitamente ou via destruidor C #), é o alvo de um WeakReference , fica no Large Object Heap, ou é especial em algum outro caminho relacionado ao gc, a única coisa identificando a memory em que Sente-se como sendo um object é a existência de referências enraizadas a ele. Caso contrário, o funcionamento do GC é análogo a tirar de um prédio tudo de valor, dinamitar o prédio, construir um novo no local do antigo e colocar todos os itens valiosos nele. O esforço necessário para dinamitar o edifício é totalmente independente da quantidade de lixo dentro dele.

Conseqüentemente, chamar o GC.Collect é capaz de aumentar a quantidade total de trabalho que o sistema precisa fazer. Isso atrasará a ocorrência da próxima coleção, mas provavelmente fará tanto trabalho imediatamente quanto a próxima coleta exigiria quando ocorresse; no ponto em que a próxima coleta teria ocorrido, a quantidade total de tempo gasto coletando teria sido aproximadamente a mesma que GC.Collect não foi chamado, mas o sistema terá acumulado algum lixo, fazendo com que a coleta posterior seja necessária mais cedo que GC.Collect não foi chamado.

As vezes que vejo GC.Collect realmente útil são quando é necessário medir o uso de memory de algum código (já que os números de uso de memory são realmente significativos após uma coleção), ou o perfil de vários algoritmos é melhor (chamando GC). Collect () antes de executar cada uma das várias partes do código pode ajudar a garantir um estado de linha de base consistente). Existem alguns outros casos em que alguém pode saber coisas que o GC não faz, mas a menos que um esteja escrevendo um programa single-threaded, não há como saber que uma chamada GC.Collect ajudaria as estruturas de dados de um thread a evitar A crise da vida “não faria com que os dados de outros segmentos” tivessem uma “crise de meia-idade” que, de outra forma, teria sido evitada.

Criando imagens em um loop – mesmo se você chamar eliminar, a memory não será recuperada. O lixo coleta todas as vezes. Eu fui de 1,7 GB de memory no meu aplicativo de processamento de fotos para 24 MB e o desempenho é excelente.

Há absolutamente tempo que você precisa chamar GC.Collect.

De fato, eu não acho que é uma prática muito ruim chamar GC.Collect.
Pode haver casos em que precisamos disso. Só por exemplo, eu tenho um formulário que executa um thread, que abre diferentes tabelas em um database, extrai o conteúdo em um campo BLOB para um arquivo temporário, criptografa o arquivo, depois lê o arquivo em um binarystream e volta para um BLOB campo em outra tabela.

Toda a operação leva bastante memory, e não é certo sobre o número de linhas e tamanho do conteúdo do arquivo nas tabelas.

Eu costumava obter OutofMemory Exception frequentemente e eu pensei que seria sensato executar periodicamente GC.Collect com base em uma variável de contador. Eu incremento um contador e quando um nível especificado é atingido, o GC é chamado para coletar qualquer lixo que pode ter sido formado e para recuperar qualquer memory perdida devido a vazamentos de memory imprevistos.

Depois disso, acho que está funcionando bem, pelo menos sem exceção !!!
Eu chamo da seguinte maneira:

 GC.Collect(GC.GetGeneration(<object utilizing the memory> /* in mycase Form itself */,GCCollectionMode.Optimized). 

Tivemos um problema semelhante com o coletor de lixo de não coletar lixo e liberar memory.

Em nosso programa, estávamos processando algumas planilhas do Excel de tamanho modesto com o OpenXML. As planilhas continham de 5 a 10 “folhas” com cerca de 1000 linhas de 14 colunas.

O programa em um ambiente de 32 bits (x86) falharia com um erro de “falta de memory”. Fizemos isso para rodar em um ambiente x64, mas queríamos uma solução melhor.

Nós encontramos um.

Aqui estão alguns fragments de código simplificados do que não funcionou e o que funcionou quando se trata de chamar explicitamente o Garbage Collector para liberar memory de objects descartados.

Chamar o GC de dentro da sub-rotina não funcionou. A memory nunca foi recuperada …

 For Each Sheet in Spreadsheets ProcessSheet(FileName,sheet) Next Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string) ' open the spreadsheet Using SLDoc as SLDocument = New SLDocument(Filename, Sheet) ' do some work.... SLDoc.Save End Using GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() End Sub 

Ao mover a chamada do GC para fora do escopo da sub-rotina, o lixo foi coletado e a memory foi liberada.

 For Each Sheet in Spreadsheets ProcessSheet(FileName,sheet) GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() Next Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string) ' open the spreadsheet Using SLDoc as SLDocument = New SLDocument(Filename, Sheet) ' do some work.... SLDoc.Save End Using End Sub 

Espero que isso ajude os outros que estão frustrados com a garbage collection do .NET quando parece ignorar as chamadas para GC.Collect() .

Paul Smith

Não há nada errado em solicitar explicitamente uma coleção. Algumas pessoas realmente querem acreditar que, se for um serviço prestado pelo fornecedor, não questionem. Ah, e todos esses congelamentos randoms nos momentos errados da sua aplicação interativa? A próxima versão vai melhorar!

Deixar um processo em segundo plano lidar com a manipulação de memory significa não ter que lidar com isso por nós mesmos, é verdade. Mas isso não significa logicamente que é melhor para nós não lidarmos com isso em todas as circunstâncias. O GC é otimizado para a maioria dos casos. Mas isso não significa logicamente que seja otimizado em todos os casos.

Você já respondeu a uma pergunta aberta como “qual é o melhor algoritmo de sorting” com uma resposta definitiva? Nesse caso, não toque no GC. Para aqueles de vocês que pediram as condições, ou deram respostas do tipo ‘neste caso’, você pode continuar a aprender sobre o GC e quando ativá-lo.

Tenho que dizer, eu tive aplicativos congelados no Chrome e no Firefox que me frustram, e mesmo assim, em alguns casos, a memory cresce sem impedimentos – Se ao menos eles aprendem a chamar o coletor de lixo – ou me deu um botão para que, quando eu começar a ler o texto de uma página, eu possa acertar e assim ficar livre de congelamentos pelos próximos 20 minutos.

Eu acho que você está certo sobre o cenário, mas não tenho certeza sobre a API.

A Microsoft diz que, em tais casos, você deve adicionar pressão de memory como uma dica para o GC que deve realizar uma coleta em breve.

O que há de errado com isso? O fato de você estar duvidando do coletor de lixo e do alocador de memory, que entre eles têm uma ideia muito maior sobre o uso de memory real do aplicativo em tempo de execução do que você.

O desejo de chamar GC.Collect () geralmente está tentando encobrir erros que você fez em outro lugar!

Seria melhor se você descobrisse onde se esqueceu de descartar as coisas de que não precisava mais.

Bottom line, você pode fazer o perfil do aplicativo e ver como essas collections adicionais afetam as coisas. Eu sugiro ficar longe dele, a menos que você vai perfil. O GC é projetado para cuidar de si mesmo e à medida que o tempo de execução evolui, eles podem aumentar a eficiência. Você não quer um monte de código por aí que pode estragar os trabalhos e não ser capaz de tirar proveito dessas melhorias. Existe um argumento semelhante para usar foreach em vez de, ou seja, que melhorias futuras sob as capas podem ser adicionadas a foreach e seu código não precisa ser alterado para aproveitar.

O .NET Framework em si nunca foi projetado para ser executado em um ambiente em tempo real. Se você realmente precisa de processamento em tempo real, você pode usar uma linguagem realtime incorporada que não seja baseada em .NET ou use o .NET Compact Framework em execução em um dispositivo Windows CE.

O pior que vai fazer é congelar o seu programa um pouco. Então, se estiver tudo bem com você, faça. Geralmente, não é necessário para aplicativos thick client ou web com maior interação do usuário.

Descobri que, às vezes, programas com threads de execução longa ou programas em lotes receberão a exceção OutOfMemory, mesmo que estejam descartando objects de forma adequada. Lembro-me de um processamento de transactions de database de linha de negócios; a outra era uma rotina de indexação em um thread de segundo plano em um aplicativo cliente espesso.

Em ambos os casos, o resultado foi simples: No GC.Collect, out of memory, consistentemente; GC.Collect, desempenho sem falhas.

Eu tentei resolver problemas de memory várias outras vezes, sem sucesso. Eu tirei isso.

Em suma, não coloque isso a menos que você esteja recebendo erros. Se você colocá-lo e não corrigir o problema de memory, retire-o. Lembre-se de testar no modo Release e comparar maçãs com maçãs.

A única vez que as coisas podem dar errado com isso é quando você se torna moralista sobre isso. Não é uma questão de valores; muitos programadores morreram e foram direto para o céu com muitos CGC desnecessários em seu código, que sobrevive a eles.