Embora eu entenda as sérias implicações de brincar com essa função (ou pelo menos é o que eu acho), não consigo ver por que ela está se tornando uma dessas coisas que programadores respeitáveis nunca usariam, mesmo aqueles que nem mesmo sabem Para que isso serve.
Digamos que eu esteja desenvolvendo um aplicativo em que o uso de memória varia extremamente, dependendo do que o usuário está fazendo. O ciclo de vida do aplicativo pode ser dividido em dois estágios principais: edição e processamento em tempo real. Durante o estágio de edição, suponha que bilhões ou mesmo trilhões de objetos sejam criados; alguns deles pequenos e outros não, alguns podem ter finalizadores e outros não, e suponha que sua vida útil varie de poucos milissegundos a longas horas. Em seguida, o usuário decide mudar para o estágio de tempo real. Nesse ponto, suponha que o desempenho desempenhe um papel fundamental e a menor alteração no fluxo do programa pode trazer consequências catastróficas. A criação de objetos é então reduzida ao mínimo possível usando pools de objetos e tal, mas então, o GC entra inesperadamente e joga tudo fora, e alguém morre.
A pergunta: neste caso, não seria sensato chamar GC.Collect () antes de entrar no segundo estágio?
Afinal, esses dois estágios nunca se sobrepõem no tempo e todas as otimizações e estatísticas que o GC poderia ter reunido seriam de pouca utilidade aqui ...
Observação: como alguns de vocês observaram, .NET pode não ser a melhor plataforma para um aplicativo como esse, mas isso está além do escopo desta pergunta. A intenção é esclarecer se uma chamada GC.Collect () pode melhorar o comportamento / desempenho geral de um aplicativo ou não. 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 bem na maioria das vezes, mas ainda é sobre adivinhação.
Obrigado.
Respostas:
Do Blog de Rico ...
Portanto, parece que essa situação pode se enquadrar na Regra # 2, você sabe que há um momento em que muitos objetos antigos morreram e isso não é recorrente. No entanto, não se esqueça das palavras de despedida de Rico.
Meça, meça, meça.
fonte
Se você chamar GC.Collect () no código de produção, você está essencialmente declarando que sabe mais do que os autores do GC. Talvez seja o caso. No entanto, geralmente não é e, portanto, fortemente desencorajado.
fonte
malloc
tem apenas uma interface muito simples, e suas implementações podem variar o suficiente para importar. Tudo depende do tipo de problema que você está tentando resolver. Semalloc
fosse "bom o suficiente", você não precisaria de buffer pooling, não é? O desenvolvimento em C / C ++ está cheio de exemplos em que você tenta adivinhar o sistema operacional / tempo de execução / bibliotecas porque você conhece melhor (e às vezes, realmente sabe). Muitos aplicativos de desempenho crítico evitam usar alocadores de sistema / tempo de execução inteiramente. Jogos usados para pré-alocar toda a memória na inicialização (arrays de tamanho constante, etc.).Então, que tal quando você estiver usando objetos COM, como MS Word ou MS Excel do .NET? Sem ligar
GC.Collect
depois de liberar os objetos COM, descobrimos que as instâncias do aplicativo Word ou Excel ainda existem.Na verdade, o código que usamos é:
Então, isso seria um uso incorreto do coletor de lixo? Se sim, como fazemos com que os objetos Interop morram? Além disso, se não foi feito para ser usado dessa forma, por que o método de
GC
éCollect
parPublic
?fonte
Bem, o GC é uma daquelas coisas com as quais tenho uma relação de amor / ódio. Nós quebramos no passado através do VistaDB e postamos em um blog sobre isso. Eles consertaram, mas leva MUITO tempo para obter soluções deles em coisas como esta.
O GC é complexo e uma abordagem de tamanho único é muito, muito difícil de aplicar em algo tão grande. A MS fez um bom trabalho, mas às vezes é possível enganar o GC.
Em geral, você não deve adicionar um a
Collect
menos que saiba com certeza que acabou de se desfazer de uma tonelada de memória e ela entrará em crise de meia-idade se o GC não limpar agora.Você pode bagunçar a máquina inteira com uma série de
GC.Collect
declarações ruins . A necessidade de uma instrução collect quase sempre aponta para um erro subjacente maior. O vazamento de memória geralmente tem a ver com referências e falta de compreensão de como funcionam. Ou usar oIDisposable
em objetos que não precisam dele e colocar uma carga muito maior no GC.Observe de perto a% de tempo gasto no GC por meio dos contadores de desempenho do sistema. Se você vir seu aplicativo usando 20% ou mais do seu tempo no GC, você tem sérios problemas de gerenciamento de objetos (ou um padrão de uso anormal). Você deseja sempre minimizar o tempo que o GC gasta, pois isso irá acelerar todo o seu aplicativo.
Também é importante observar que o GC é diferente em servidores e em estações de trabalho. Eu vi uma série de pequenos problemas difíceis de rastrear com pessoas que não testam os dois (ou nem mesmo sabem que são dois).
E apenas para ser o mais completo possível em minha resposta, você também deve testar no Mono se estiver direcionando essa plataforma também. Uma vez que é uma implementação totalmente diferente, pode ocorrer problemas totalmente diferentes da implementação do MS.
fonte
Existem situações em que é útil, mas em geral deve ser evitado. Você poderia compará-lo ao GOTO, ou andar de motocicleta: você faz isso quando precisa, mas não conta para seus amigos.
fonte
Pela minha experiência, nunca foi aconselhável fazer uma chamada para GC.Collect () no código de produção. Na depuração, sim, ele tem suas vantagens em ajudar a esclarecer possíveis vazamentos de memória. Acho que 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 sinto que preciso chamar GC.Collect (), é uma pista de que saí do caminho algum lugar. Na sua situação, não parece que você realmente tenha problemas de memória, apenas que está preocupado com a instabilidade que a coleção trará para o seu processo. Visto que ele não limpa os objetos ainda em uso e que se adapta muito rapidamente às demandas crescentes e decrescentes, acho que você não terá que se preocupar com isso.
fonte
Um dos maiores motivos para chamar GC.Collect () é quando você acaba de realizar um evento significativo que cria muito lixo, como o que você descreveu. 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 criar um perfil e ver por si mesmo.
fonte
Bem, obviamente você não deve escrever código com requisitos de tempo real em linguagens com coleta de lixo em tempo não 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 para encobrir os problemas em um estilo cult de carga, e adicioná-lo indiscriminadamente causará problemas de desempenho.
fonte
Chamar GC.Collect () força o CLR a fazer um rastreamento de pilha para ver se cada objeto pode ser realmente liberado verificando as referências. Isso afetará a escalabilidade se o número de objetos for alto e também é conhecido por acionar a coleta de lixo com muita frequência. Confie no CLR e deixe o coletor de lixo rodar sozinho quando apropriado.
fonte
Na verdade, não acho uma prática muito ruim ligar para GC.Collect.
Pode haver casos em que precisamos disso. Por exemplo, eu tenho um formulário que executa um thread, que por sua vez abre tabelas diferentes em um banco de dados, extrai o conteúdo de um campo BLOB para um arquivo temporário, criptografa o arquivo e, em seguida, lê o arquivo em um fluxo binário e de volta em um BLOB campo em outra tabela.
Toda a operação consome muita memória e não é certo sobre o número de linhas e o tamanho do conteúdo do arquivo nas tabelas.
Eu costumava obter OutofMemory Exception com frequência e pensei que seria sábio executar GC.Collect periodicamente com base em uma variável de contador. Eu incremento um contador e quando um nível especificado é alcançado, o GC é chamado para coletar qualquer lixo que possa ter se formado e para recuperar qualquer memória perdida devido a vazamentos de memória imprevistos.
Depois disso, acho que está funcionando bem, pelo menos sem exceção !!!
Eu ligo da seguinte maneira:
fonte
Em .net, o tempo necessário para realizar uma coleta de lixo está muito mais fortemente relacionado à quantidade de coisas que não são lixo do que à quantidade de coisas que são. Na verdade, a menos que um objeto seja substituído
Finalize
(explicitamente ou por meio do destruidor C #), seja o alvo de a, estejaWeakReference
no Large Object Heap ou seja especial em alguma outra forma relacionada ao gc, a única coisa que identifica a memória na qual está como objeto é a existência de referências enraizadas a ele. Caso contrário, a operação do GC é análoga a tirar de um edifício tudo de valor e dinamitar o edifício, construir um novo no local do antigo e colocar todos os itens valiosos nele. O esforço necessário para dinamitar o prédio é totalmente independente da quantidade de lixo dentro dele.Conseqüentemente, a chamada
GC.Collect
tende a aumentar a quantidade total de trabalho que o sistema precisa realizar. Isso atrasará a ocorrência da próxima coleção, mas provavelmente fará tanto trabalho imediatamente quanto a próxima coleção teria exigido quando ocorreu; no ponto em que a próxima coleta teria ocorrido, a quantidade total de tempo gasto coletando terá sido aproximadamente o mesmo queGC.Collect
não foi chamado, mas o sistema terá acumulado algum lixo, fazendo com que a coleta seguinte seja exigida mais cedo do queGC.Collect
não foi chamado.Os momentos em que posso ver que são
GC.Collect
realmente úteis são quando é necessário medir o uso de memória de algum código (uma vez que os números de uso de memória só são realmente significativos após uma coleção) ou o perfil de qual dos 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 sabe, mas a menos que se esteja escrevendo um programa de thread único, não há como saber que umaGC.Collect
chamada que ajudaria as estruturas de dados de um thread a evitar "crise de meia-idade "não faria com que os dados de outros tópicos tivessem uma" crise de meia-idade "que de outra forma teria sido evitada.fonte
Criação de imagens em loop - mesmo se você chamar dispose, a memória não será recuperada. Recolha de lixo sempre. Passei de 1,7 GB de memória em meu aplicativo de processamento de fotos para 24 MB e o desempenho é excelente.
Você precisa absolutamente de tempo para ligar para GC.Collect.
fonte
Dispose
é não deveria liberar memória gerenciada. Você parece não saber como funciona o modelo de memória do .NET.Tivemos um problema semelhante com o coletor de lixo não coletando lixo e liberando memória.
Em nosso programa, estávamos processando algumas planilhas do Excel de tamanho modesto com OpenXML. As planilhas continham de 5 a 10 "planilhas" com cerca de 1000 linhas de 14 colunas.
O programa em um ambiente de 32 bits (x86) travaria com um erro de "falta de memória". Conseguimos executá-lo em um ambiente x64, mas queríamos uma solução melhor.
Encontramos um.
Aqui estão alguns fragmentos de código simplificado do que não funcionou e do que funcionou quando se trata de chamar explicitamente o Garbage Collector para liberar memória de objetos descartados.
Chamar o GC de dentro da sub-rotina não funcionou. A memória nunca foi recuperada ...
Movendo a chamada GC para fora do escopo da sub-rotina, o lixo foi coletado e a memória foi liberada.
Espero que isso ajude outras pessoas que estão frustradas com a coleta de lixo do .NET quando ela parece ignorar as chamadas para
GC.Collect()
.Paul Smith
fonte
Não há nada de errado em chamar explicitamente para uma coleção. Algumas pessoas realmente querem acreditar que, se for um serviço prestado pelo fornecedor, não o questione. Ah, e todos aqueles congelamentos aleatórios nos momentos errados do seu aplicativo interativo? A próxima versão o tornará melhor!
Deixar que um processo em segundo plano lide com a manipulação da memória significa não ter que lidar com isso nós mesmos, é verdade. Mas isso não significa logicamente que seja melhor para nós não lidarmos com isso nós mesmos, 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 classificação' com uma resposta definitiva? Nesse caso, não toque no GC. Para aqueles de vocês que solicitaram as condições ou deram respostas do tipo 'neste caso', podem prosseguir para aprender sobre o GC e quando ativá-lo.
Tenho que dizer que tive congelamentos de aplicativos no Chrome e no Firefox que me deixaram muito frustrado e, mesmo assim, em alguns casos, a memória cresce sem obstáculos - se eles aprendessem a chamar o coletor de lixo - ou me dessem um para que, ao começar a ler o texto de uma página, eu possa acertá-lo e, assim, ficar livre de congelamentos pelos próximos 20 minutos.
fonte
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 memória como uma dica ao GC de que ele deve realizar uma coleta em breve.
fonte
O que há de errado com isso? O fato de você estar adivinhando o coletor de lixo e o alocador de memória, que entre eles têm uma ideia muito maior sobre o uso real de memória do aplicativo em tempo de execução do que você.
fonte
O desejo de chamar GC.Collect () geralmente é tentar encobrir os erros que você cometeu em outro lugar!
Seria melhor se você encontrasse onde esqueceu de descartar as coisas que não precisa mais.
fonte
Resumindo, você pode criar o perfil do aplicativo e ver como essas coleções adicionais afetam as coisas. Eu sugiro ficar longe dele, a menos que você vá para o perfil. O GC é projetado para cuidar de si mesmo e, conforme 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 atrapalhar o trabalho e não ser capaz de tirar vantagem dessas melhorias. Há um argumento semelhante para usar foreach em vez de para, sendo que futuras melhorias ocultas podem ser adicionadas a foreach e seu código não precisa ser alterado para tirar proveito.
fonte
O .NET Framework em si nunca foi projetado para ser executado em um ambiente de tempo real. Se você realmente precisa do processamento em tempo real, pode usar uma linguagem de tempo real incorporada que não seja baseada em .NET ou usar o .NET Compact Framework em execução em um dispositivo Windows CE.
fonte
O pior que pode fazer é congelar um pouco o seu programa. Então, se estiver tudo bem para você, faça. Normalmente não é necessário para aplicativos de cliente ou web com interação principalmente do usuário.
Eu descobri que às vezes programas com threads de longa execução, ou programas em lote, obterão a exceção OutOfMemory, embora estejam descartando objetos corretamente. Lembro-me de um processamento de transações de banco de dados 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: Não GC.Collect, out of memory, consistentemente; GC.Collect, desempenho perfeito.
Tentei resolver problemas de memória várias outras vezes, sem sucesso. Eu tirei.
Resumindo, não coloque, a menos que esteja recebendo erros. Se você colocá-lo e ele não resolver o problema de memória, 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 é quando você se torna moralista a respeito. Não é uma questão de valores; muitos programadores morreram e foram direto para o céu com muitos GC.Collects desnecessários em seus códigos, que sobrevivem a eles.
fonte