Quando System.gc () faz algo?

118

Eu sei que a coleta de lixo é automatizada em Java. Mas eu entendi que se você chamar System.gc()seu código, a JVM pode ou não decidir realizar a coleta de lixo naquele ponto. Como isso funciona exatamente? Em que base / parâmetros exatamente a JVM decide fazer (ou não fazer) um GC quando vê System.gc()?

Há algum exemplo em que é uma boa ideia colocar isso em seu código?

Michael
fonte
4
Um conceito muito complexo para todos os detalhes aqui, mas este artigo deve ajudá-lo: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET
O comportamento exato depende da JVM, ou seja, depende da implementação.
Thorbjørn Ravn Andersen

Respostas:

59

Na prática, geralmente decide fazer uma coleta de lixo. A resposta varia dependendo de muitos fatores, como em qual JVM você está executando, em qual modo está e qual algoritmo de coleta de lixo está usando.

Eu não dependeria disso em seu código. Se a JVM estiver prestes a lançar um OutOfMemoryError, chamar System.gc () não o parará, porque o coletor de lixo tentará liberar o máximo que puder antes de chegar a esse extremo. A única vez que vi isso ser usado na prática é em IDEs, onde é anexado a um botão no qual o usuário pode clicar, mas mesmo aí não é muito útil.

Jodonnell
fonte
3
você pode forçar uma coleta de lixo no jconsole
Benedikt Waldvogel
25
@bene Você pode realmente "forçar" isso? O jconsole está apenas chamando System.gc ()?
John Meagher
4
Eu usaria System.gc()enquanto estava em uma tela de carregamento de um videogame. Nesse ponto, eu realmente não me importaria se a chamada cancelasse tudo o que pudesse ou não fizesse nada. Eu, no entanto, preferiria que ele "se esforçasse para reciclar objetos não usados" durante a tela de carregamento, em vez de durante o jogo.
Kröw
30

O único exemplo que posso pensar em que faz sentido chamar System.gc () é ao criar o perfil de um aplicativo para pesquisar possíveis vazamentos de memória. Acredito que os criadores de perfil chamam esse método antes de fazer um instantâneo da memória.

Guillermo Vasconcelos
fonte
4
Sim, é o que eu também faço. Os valores retornados por Runtime.freeMemory (), por exemplo, só são realmente significativos após System.gc (), porque normalmente você deseja saber quanta memória está bloqueada por instâncias não coletáveis. Para ser usado apenas em depuração / perfil de memória, é claro, nunca em produção.
sleske
Não, é bom para muitos outros cenários também. Por exemplo, digamos que você tenha um único padrão que reconhece o ciclo de vida. Em onStop (), se você anular (anular) a instância, é uma boa ideia chamar System.gc () para ajudá-lo.
portfólio de
26

A especificação da linguagem Java não garante que a JVM iniciará um GC quando você ligar System.gc(). Esta é a razão de que "pode ​​ou não decidir fazer um GC naquele momento".

Agora, se você olhar o código-fonte do OpenJDK , que é o backbone do Oracle JVM, verá que uma chamada para System.gc()inicia um ciclo de GC. Se você usa outra JVM, como J9, você deve verificar sua documentação para descobrir a resposta. Por exemplo, a JVM da Azul tem um coletor de lixo que roda continuamente, então uma chamada para System.gc()não fará nada

Algumas outras respostas mencionam o início de um GC no JConsole ou VisualVM. Basicamente, essas ferramentas fazem uma chamada remota para System.gc().

Normalmente, você não deseja iniciar um ciclo de coleta de lixo do seu código, pois isso bagunça a semântica do seu aplicativo. Seu aplicativo faz algumas coisas de negócios, a JVM cuida do gerenciamento de memória. Você deve manter essas questões separadas (não faça seu aplicativo fazer algum gerenciamento de memória, concentre-se nos negócios).

No entanto, existem alguns casos em que uma chamada para System.gc()pode ser compreensível. Considere, por exemplo, microbenchmarks. Ninguém quer que um ciclo de GC aconteça no meio de um microbenchmark. Portanto, você pode acionar um ciclo de GC entre cada medição para garantir que cada medição comece com uma pilha vazia.

Pierre Laporte
fonte
26

Você não tem controle sobre GC em java - a VM decide. Nunca encontrei um caso em que System.gc()fosse necessário . Uma vez que uma System.gc()chamada simplesmente SUGERE que a VM faça uma coleta de lixo e também uma coleta de lixo COMPLETA (gerações antigas e novas em um heap multigeracional), ela pode, na verdade, fazer com que MAIS ciclos de CPU sejam consumidos do que o necessário.

Em alguns casos, pode fazer sentido sugerir à VM que faça uma coleta completa AGORA, pois você pode saber que o aplicativo ficará ocioso pelos próximos minutos antes de ocorrer um trabalho pesado. Por exemplo, logo após a inicialização de muitos objetos temporários durante a inicialização do aplicativo (ou seja, acabei de armazenar em cache TONELADAS de informações e sei que não terei muita atividade por um minuto ou mais). Pense em um IDE como o eclipse inicializando - ele faz muito para inicializar, então talvez imediatamente após a inicialização faça sentido fazer um gc completo naquele ponto.

DustinB
fonte
17

Você precisa ter muito cuidado ao ligar System.gc(). Chamá-lo pode adicionar problemas de desempenho desnecessários ao seu aplicativo e não é garantido que realmente execute uma coleção. Na verdade, é possível desativar o explícito System.gc()por meio do argumento java -XX:+DisableExplicitGC.

Recomendo enfaticamente a leitura dos documentos disponíveis em Java HotSpot Garbage Collection para obter detalhes mais aprofundados sobre a coleta de lixo.

David Schlosnagle
fonte
Meu aplicativo Android sempre me lança uma OutOfMemoryException. Então, eu estava pensando em usar System.gc () na função onDestroy da minha classe para limpar todas as referências e objetos indesejados. É uma boa abordagem
Sagar Devanga
2
@Sagar Devanga Chamar manualmente o gc conforme você descreve dificilmente ajudará. Antes de lançar um OOM, o JVM já terá tentado coletar o lixo para liberar memória
Scrubbie
10

System.gc()é implementado pela VM e o que faz é específico da implementação. O implementador poderia simplesmente retornar e não fazer nada, por exemplo.

Quanto ao momento de emitir uma coleta manual, o único momento em que você pode querer fazer isso é quando você abandona uma grande coleção que contém um monte de coleções menores - Map<String,<LinkedList>> por exemplo - e deseja tentar obter o golpe de desempenho então e lá, mas na maior parte, você não deve se preocupar com isso. O GC sabe melhor do que você - infelizmente - na maioria das vezes.

Patrick
fonte
8

Se você usar buffers de memória direta, a JVM não executa o GC para você, mesmo se você estiver com pouca memória direta.

Se você chamar ByteBuffer.allocateDirect()e obtiver um OutOfMemoryError, poderá descobrir que esta chamada está bem após acionar um GC manualmente.

Peter Lawrey
fonte
Você está dizendo que System.gc () coleta alocações diretas enquanto a JVM normalmente não (antes de lançar um OOM). Porque eu acho que isso está errado. A única razão pela qual uma alocação pode ser bem-sucedida após um GC explícito é o tempo adicional e um pouco mais de probabilidade de um objeto in-heap com um finalizador sendo liberado (e liberando algum objeto alocado diretamente).
eckes
Na versão mais recente do Java 6, o código em allocateBuffer sempre executará um System.gc () antes de lançar um OutOfMemoryError. No código de finalização, há um atalho para o Cleanersqual a memória direta usa. ou seja, não é liberado pelo thread finalizador, pois pode ser muito lento. Mesmo assim, é possível obter um OOME se você estiver criando regiões de memória direta particularmente rápido. A solução é não fazer isso e reutilizar suas regiões de memória direta onde for possível.
Peter Lawrey
1
obrigado, isso parece mais com a minha experiência. Portanto, acho que a resposta está correta apenas para versões anteriores do Java.
eckes
5

A maioria das JVMs iniciará um GC (dependendo do switch -XX: DiableExplicitGC e -XX: + ExplicitGCInvokesConcurrent). Mas a especificação é apenas menos bem definida para permitir implementações melhores posteriormente.

A especificação precisa de esclarecimento: Bug # 6668279: (spec) System.gc () deve indicar que não recomendamos o uso e não garantimos o comportamento

Internamente, o método gc é usado por RMI e NIO, e eles requerem execução síncrona, que: isso está atualmente em discussão:

Bug # 5025281: Permitir que System.gc () acione coleções completas simultâneas (não pare o mundo)

eckes
fonte
3

Garbage Collectioné bom em Java, se estivermos executando software codificado em java em desktop / laptop / servidor. Você pode ligar System.gc()ou Runtime.getRuntime().gc()entrarJava .

Observe que nenhuma dessas chamadas tem garantia de efeito. Eles são apenas uma sugestão para o jvm executar o Coletor de Lixo. Depende do JVM se ele executa o GC ou não. Portanto, uma resposta curta: não sabemos quando é executado. Resposta mais longa: JVM executaria gc se tivesse tempo para isso.

Acredito que o mesmo se aplica ao Android. No entanto, isso pode tornar seu sistema mais lento.

Naveen
fonte
O JVM executaria o gc se tivesse tempo para isso : nem sempre é verdade, o Jvm pode executar o gc quando a memória do Eden está cheia.
abdelmouheimen
2

Normalmente, a VM faria uma coleta de lixo automaticamente antes de lançar uma OutOfMemoryException, portanto, adicionar uma chamada explícita não deve ajudar, exceto pelo fato de que talvez mova o impacto de desempenho para um momento anterior no tempo.

No entanto, acho que encontrei um caso em que pode ser relevante. Não tenho certeza, pois ainda tenho que testar se tem algum efeito:

Quando você mapeia a memória de um arquivo, acredito que a chamada map () lança uma IOException quando um bloco de memória grande o suficiente não está disponível. Uma coleta de lixo antes do arquivo map () pode ajudar a prevenir isso, eu acho. O que você acha?


fonte
2

nunca podemos forçar a coleta de lixo. System.gc está apenas sugerindo vm para coleta de lixo, entretanto, realmente a que horas o mecanismo é executado, ninguém sabe, isso é como afirmado pelas especificações JSR.

lwpro2
fonte
2

Há MUITO a ser dito sobre reservar um tempo para testar as várias configurações de coleta de lixo, mas, como foi mencionado acima, geralmente não é útil fazer isso.

Atualmente, estou trabalhando em um projeto que envolve um ambiente de memória limitada e uma quantidade relativamente grande de dados - existem algumas grandes partes de dados que levam meu ambiente ao seu limite, e embora eu tenha conseguido reduzir o uso de memória de forma que, em teoria, deveria funcionar bem, eu ainda obteria erros de espaço de heap --- as opções detalhadas do GC me mostraram que ele estava tentando coletar o lixo, mas sem sucesso. No depurador, eu poderia executar System.gc () e com certeza haveria "abundância" de memória disponível ... não muito extra, mas o suficiente.

Consequentemente, a única vez que meu aplicativo chama System.gc () é quando está prestes a inserir o segmento de código onde grandes buffers necessários para o processamento dos dados serão alocados e um teste na memória livre disponível indica que não estou garantido tê-lo. Em particular, estou olhando para um ambiente de 1 GB onde pelo menos 300 MB são ocupados por dados estáticos, com a maior parte dos dados não estáticos relacionados à execução, exceto quando os dados sendo processados ​​são de pelo menos 100-200 MB em a fonte. Tudo faz parte de um processo de conversão automática de dados, portanto, todos os dados existem por períodos relativamente curtos de tempo no longo prazo.

Infelizmente, embora as informações sobre as várias opções para ajustar o coletor de lixo estejam disponíveis, parece um processo amplamente experimental e as especificações de nível inferior necessárias para entender como lidar com essas situações específicas não são facilmente obtidas.

Dito tudo isso, embora eu esteja usando System.gc (), eu ainda continuei a ajustar usando parâmetros de linha de comando e consegui melhorar o tempo de processamento geral do meu aplicativo em uma quantidade relativamente significativa, apesar de ser incapaz de superar o obstáculo representado pelo trabalho com os blocos maiores de dados. Dito isto, System.gc () é uma ferramenta .... uma ferramenta muito pouco confiável, e se você não for cuidadoso com a forma como a usa, vai desejar que não funcione com mais freqüência.

Kyune
fonte
2

Se você quiser saber se o seu System.gc()é chamado, você pode, com a nova atualização 4 do Java 7, receber uma notificação quando a JVM executar a coleta de lixo.

Não estou 100% certo de que a classe GarbageCollectorMXBean foi introduzida na atualização 4 do Java 7, porque não consegui encontrá-la nas notas de lançamento, mas encontrei as informações no site javaperformancetuning .com

Shervin Asgari
fonte
0

Não consigo pensar em um exemplo específico em que seja bom executar o GC explícito.

Em geral, a execução de GC explícito pode realmente causar mais danos do que benefícios, porque um gc explícito acionará uma coleção completa, que leva muito mais tempo à medida que passa por cada objeto. Se esse gc explícito acabar sendo chamado repetidamente, pode facilmente levar a um aplicativo lento, pois muito tempo é gasto executando GCs completos.

Alternativamente, se estiver examinando o heap com um analisador de heap e você suspeitar que um componente da biblioteca está chamando GC explícito, você pode desativá-lo adicionando: gc = -XX: + DisableExplicitGC aos parâmetros JVM.

zxcv
fonte
2
Chamamos System.gc () para tentar fechar nossos objetos MappedByteBuffer que de outra forma não estão fechados e, portanto, não estão disponíveis para outros processos ou para truncamento de arquivo, embora chamemos 'close ()' nos objetos. Infelizmente, nem sempre os fecha.
Tim Cooper
0

De acordo com o método Thinking in Java de Bruce Eckel, um caso de uso para uma chamada System.gc () explícita é quando você deseja forçar a finalização, ou seja, a chamada para o método finalize .

Asterisco
fonte
NB: existe um método próprio para forçar a execução da finalização. (Na verdade, o problema não está em executar finalizadores, mas em reconhecer que um objeto pode ser finalizado porque não tem referência)
verifica
-1

enquanto o system.gc funciona, ele irá parar o mundo: todas as respostas são interrompidas para que o coletor de lixo possa verificar cada objeto para verificar se é necessário excluí-lo. se o aplicativo for um projeto da web, todas as solicitações serão interrompidas até que o gc termine, e isso fará com que seu projeto da web não funcione em um momento.

fletura
fonte