Coleta de lixo Java G1 em produção

91

Como o Java 7 vai usar a nova coleta de lixo G1 por padrão, o Java será capaz de lidar com uma ordem de magnitude maior sem tempos de pausa de GC supostamente "devastadores"? Alguém já implementou o G1 na produção, quais foram suas experiências?

Para ser justo, a única vez que vi pausas de GC realmente longas foi em heaps muito grandes, muito mais do que uma estação de trabalho teria. Para esclarecer minha pergunta; O G1 abrirá o portal para pilhas na casa das centenas de GB? TB?

Benstpierre
fonte
15
Embora pudesse ser reformulado mais especificamente, esta não é uma pergunta horrível. Eu realmente gostaria que as pessoas tivessem que se explicar melhor do que "Não é uma pergunta" ao votar para fechar.
Bill K
Não votei para fechar, mas gostaria que o OP tivesse feito um trabalho mais objetivo ao detalhar suas queixas com o GC atual. Além disso, "Java" é uma linguagem enquanto ele está falando de uma implementação, e eu não sei o que "implementar G1 na produção" significa, especialmente com o tempo futuro do resto da questão. Se for no Java 7, certamente ninguém o usou na produção?
Pascal Cuoq
6
@Pascal G1 é um recurso experimental disponível no JDK desde a atualização 14 do JDK 6. Por "implementar o G1 na produção", acho que ele quis dizer realmente usá-lo, não é tão difícil de imaginar. E embora eu concorde que G1 é parte do JDK 7, não Java, uma pesquisa por Java 7 no Google retorna a página inicial do JDK 7 como seu primeiro resultado, e ambos os termos são freqüentemente usados ​​alternadamente. @Benju Eu não confiaria nos resultados obtidos com o G1 no JDK atual, pois é experimental, muitas coisas podem mudar de agora para o lançamento oficial.
teto
2
Parece que o JDK 7, incluindo as atualizações 1,2 e 3, não usa o G1 gc por padrão. Você pode checá-lo por jinfo -flag UseG1GC pid
George

Respostas:

34

Parece que o objetivo do G1 é ter tempos de pausa menores, até o ponto em que tenha a capacidade de especificar um objetivo de tempo máximo de pausa.

A coleta de lixo não é mais apenas um negócio simples de "Ei, está cheio, vamos mover tudo de uma vez e começar de novo" - é um sistema de encadeamento em segundo plano incrivelmente complexo, de vários níveis. Ele pode fazer grande parte de sua manutenção em segundo plano, sem nenhuma pausa, e também usa o conhecimento dos padrões esperados do sistema em tempo de execução para ajudar - como assumir que a maioria dos objetos morre logo após serem criados, etc.

Eu diria que os tempos de pausa do GC continuarão a melhorar, e não piorar, com versões futuras.

EDITAR:

ao reler, ocorreu-me que uso Java diariamente - Eclipse, Azureus e os aplicativos que desenvolvo, e já faz MUITO TEMPO desde que vi uma pausa. Não é uma pausa significativa, mas quero dizer qualquer pausa.

Já vi pausas quando clico com o botão direito do mouse no Windows Explorer ou (ocasionalmente) quando conecto determinados hardwares USB, mas com Java --- nenhum.

O GC ainda é um problema para alguém?

Bill K
fonte
Concordo - a única vez que vi pausas de GC foi quando, deliberada ou acidentalmente, as
provoquei com um
28
Sim, o GC ainda é um problema quando você começa a lidar com grandes heaps (> 16 GB), especialmente com grandes gerações efetivas.
The Alchemist de
2
@ the-alquhemist uau, eu vi seu comentário de passagem algumas vezes e me ocorreu que você disse 16 GB !! Embora eu tenha certeza absoluta de que você está certo de que isso pode causar grandes atrasos, quero verificar se você desativou TODAS as trocas. Em um sistema de grande memória, qualquer troca de java irá absolutamente matar seu sistema (porque o GC é muito hostil para troca). Tenho certeza de que você já fez isso, mas eu só queria mencionar isso - porque faria uma grande diferença. Nunca vi um PC com tanta memória RAM - quanto você tem? 32g?
Bill K
8
Sim, os GCs são problemáticos para serviços porque são o que torna MUITO difícil melhorar os limites de TP99.9 (e superiores). Especificamente, os GCs da "velha geração" podem ser armadilhas mortais que quase congelam a JVM (e o serviço) por vários segundos; e para serviços que normalmente atendem a solicitações em milisegundos de um dígito (ou dois dígitos baixos), isso é problemático. Pelo que vale a pena, este foi um problema prático com o armazenamento de back-end usado pelo serviço Simple Queue da Amazon (não é possível entrar em muitos detalhes, pois é interno da AWS).
StaxMan de
21
O chato sobre o GC é que a Azul inventou anos atrás um engenhoso algoritmo de GC (Azul C4) que pode facilmente lidar com centenas de gigabytes sem nenhum tempo de pausa perceptível, fazendo uso muito inteligente do hardware de memória do processador. Mas ninguém sabe disso e não será implementado nas versões principais do Java em breve, pois precisa de algum suporte do sistema operacional. E os fornecedores de sistemas operacionais não farão nada até que as pessoas conheçam o algoritmo e pressionem os fornecedores de sistemas operacionais. Consulte azulsystems.com/zing/pgc , managedruntime.org
Hans-Peter Störr
58

Tenho testado com um aplicativo pesado: 60-70 GB alocados para heap, com 20-50 GB em uso a qualquer momento. Com esses tipos de aplicativos, é um eufemismo dizer que sua milhagem pode variar. Estou executando o JDK 1.6_22 no Linux. As versões secundárias são importantes - antes de cerca de 1.6_20, havia erros no G1 que causavam NullPointerExceptions aleatórios.

Descobri que é muito bom manter a meta de pausa que você atribui na maior parte do tempo. O padrão parece ser uma pausa de 100 ms (0,1 segundo), e venho dizendo para ele fazer a metade disso (-XX: MaxGCPauseMillis = 50). No entanto, quando fica realmente com pouca memória, ele entra em pânico e faz uma coleta de lixo completa para o mundo inteiro. Com 65 GB, isso leva entre 30 segundos e 2 minutos. (O número de CPUs provavelmente não faz diferença; provavelmente é limitado pela velocidade do barramento.)

Comparado com o CMS (que não é o servidor GC padrão, mas deveria ser para servidores da web e outros aplicativos em tempo real), as pausas típicas são muito mais previsíveis e podem ser muito mais curtas. Até agora estou tendo mais sorte com o CMS para as grandes pausas, mas isso pode ser aleatório; Estou vendo-os apenas algumas vezes a cada 24 horas. Não tenho certeza de qual será o mais adequado no meu ambiente de produção no momento, mas provavelmente G1. Se a Oracle continuar ajustando, eu suspeito que G1 acabará sendo o vencedor.

Se você não está tendo problemas com os coletores de lixo existentes, não há razão para considerar o G1 agora. Se você estiver executando um aplicativo de baixa latência, como um aplicativo GUI, G1 é provavelmente a escolha certa, com MaxGCPauseMillis definido muito baixo. Se você está executando um aplicativo em lote, G1 não compra nada.

David Leppik
fonte
14

Embora eu não tenha testado o G1 em produção, pensei em comentar que os GCs já são problemáticos para casos sem pilhas "gigantescas". Especificamente, serviços com apenas, digamos, 2 ou 4 GB podem ser severamente afetados pelo GC. Os GCs de geração jovem geralmente não são problemáticos, pois terminam em milissegundos de um dígito (ou no máximo dois dígitos). Mas as coleções da velha geração são muito mais problemáticas, pois levam vários segundos com tamanhos da velha geração de 1 GB ou mais.

Agora: em teoria o CMS pode ajudar muito nisso, pois pode executar grande parte de sua operação simultaneamente. No entanto, com o tempo, haverá casos em que ele não poderá fazer isso e terá que voltar para a coleta "parar o mundo". E quando isso acontecer (depois de, digamos, 1 hora - não com frequência, mas ainda com muita frequência), bem, segure a porra do chapéu. Isso pode levar um minuto ou mais. Isso é especialmente problemático para serviços que tentam limitar a latência máxima; em vez de levar, digamos, 25 milissegundos para atender a uma solicitação, agora leva dez segundos ou mais. Adicionar injuria aos clientes insultuosos, muitas vezes, expira o pedido e tenta novamente, levando a mais problemas (também conhecido como "tempestade de merda").

Essa é uma área em que o G1 esperava ajudar muito. Trabalhei para uma grande empresa que oferece serviços em nuvem para armazenamento e envio de mensagens; e não podíamos usar o CMS porque, embora na maior parte do tempo funcionasse melhor do que as variedades paralelas, apresentava esses colapsos. Então, por cerca de uma hora as coisas estavam boas; e então as coisas caíram no ventilador ... e como o serviço era baseado em clusters, quando um nó tinha problemas, outros normalmente o seguiam (já que os tempos limite induzidos por GC levam a outros nós acreditam que o nó havia travado, levando a redirecionamentos).

Não acho que GC seja um grande problema para aplicativos, e talvez até serviços não agrupados sejam afetados com menos frequência. Porém, mais e mais sistemas são agrupados (especialmente graças aos armazenamentos de dados NoSQL) e os tamanhos de heap estão crescendo. Os GCs OldGen são superlinearmente relacionados ao tamanho do heap (o que significa que dobrar o tamanho do heap mais do que duplica o tempo do GC, assumindo que o tamanho do conjunto de dados ativo também dobra).

StaxMan
fonte
13

O CTO da Azul, Gil Tene, tem uma boa visão geral dos problemas associados à coleta de lixo e uma revisão de várias soluções em sua apresentação Entendendo a coleta de lixo do Java e o que você pode fazer sobre ela , e há detalhes adicionais neste artigo: http: // www.infoq.com/articles/azul_gc_in_detail .

O Coletor de Lixo C4 da Azul em nossa JVM Zing é paralelo e simultâneo e usa o mesmo mecanismo de GC para a nova e velha geração, trabalhando simultaneamente e compactando em ambos os casos. Mais importante ainda, o C4 não tem um retrocesso que impede o mundo. Toda compactação é executada simultaneamente com o aplicativo em execução. Temos clientes executando muito grandes (centenas de GBytes) com tempos de pausa de GC de pior caso de <10 mseg e, dependendo do aplicativo, muitas vezes menos de 1-2 mseg.

O problema com CMS e G1 é que em algum ponto a memória heap Java deve ser compactada e ambos os coletores de lixo param o mundo / STW (ou seja, pausam o aplicativo) para realizar a compactação. Portanto, embora o CMS e o G1 possam aumentar as pausas STW, eles não as eliminam. O C4 da Azul, no entanto, elimina completamente as pausas STW e é por isso que Zing tem pausas de GC tão baixas, mesmo para tamanhos de heap gigantescos.

E para corrigir uma afirmação feita em uma resposta anterior, o Zing não exige nenhuma alteração no sistema operacional. Ele funciona como qualquer outra JVM em distros Linux não modificadas.

Scott Sellers
fonte
3
Eu só me pergunto como o C4 da Azul conseguiu o que você disse e por que a Sun ou a Oracle não conseguiram. Existe algum grande segredo ou isso é apenas uma espécie de troca?
George,
5
O C4 da Azul tem uma tecnologia única que tem suas origens nos dispositivos de computação de hardware da Azul (que usa processadores especializados construídos para executar aplicativos Java corporativos) e foi desenvolvida para rodar em servidores x86 regulares executando Linux. Todos os outros coletores de lixo de classe empresarial (seja da Oracle ou IBM) em algum ponto devem fazer pausas de interrupção - o atributo exclusivo do C4 da Azul é que ele nunca faz essas pausas STW problemáticas. Se você está curioso, os inventores do coletor C4 publicaram um artigo sobre como ele funciona: dl.acm.org/citation.cfm?id=1064988 .
Scott Sellers,
Scott, li aqui no blog.mikemccandless.com/2012/07/… que a Azul fornece um módulo de kernel que pré-aloca memória para uso de JVM. Não é verdade? Se for verdade, não é muito uma modificação do kernel, mas ainda uma modificação.
Dan Pritts
4
George, duas palavras: protegido por patente. Dan, quando você compra o Zing, parte do que você paga é que o pessoal de suporte o ajuste para a sua aplicação - e isso inclui a alocação do uso geral da memória do sistema. O próprio módulo do kernel evita gravações em um bloco de memória que está sendo coletado como lixo. Esse é o molho secreto que o torna "sem pausa": os threads só param se tentarem gravar em um desses blocos, e apenas o tempo suficiente para compactar esse bloco.
David Leppik de
13

Já estamos usando o G1GC, há quase dois anos. Está indo muito bem em nosso sistema de processamento de transações de missão crítica e provou ser um ótimo suporte para alto rendimento, poucas pausas, simultaneidade e gerenciamento otimizado de memória pesada.

Estamos usando as seguintes configurações de JVM:

-server -Xms512m -Xmx3076m -XX:NewRatio=50 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:+AggressiveOpts -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000 -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime

Atualizada

-d64 -server -Xss4m -Xms1024m -Xmx4096m -XX:NewRatio=50 -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:-DisableExplicitGC -XX:+AggressiveOpts -Xnoclassgc -XX:+UseNUMA -XX:+UseFastAccessorMethods -XX:ReservedCodeCacheSize=48m -XX:+UseStringCache -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000
emkays
fonte
5
No Java 8, você não precisa definir -XX: + UseCompressedOops ou -XX: + DoEscapeAnalysis, o booth está ativado como padrão. Consulte: docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
Mirko Ebert
8

O coletor G1 reduz o impacto de coleções completas. Se você tiver um aplicativo em que já reduziu a necessidade de coletas completas, o coletor de varredura de mapa simultâneo é tão bom e, em minha experiência, tem tempos menores de coleta menores.

Peter Lawrey
fonte
"observe que o uso de produção de G1 só é permitido onde um contrato de suporte Java foi adquirido.", groups.google.com/forum/#!topic/javaposse/Vm0a4H-QY54 , então é um mito ou não?
Christophe Roussy de
1
@ChristopheRoussy Não sei mais se isso é verdade (ou na verdade tenho provas de que sempre foi verdade). Não requer o -XX: + UnlockCommercialFeatures, então suspeito que G1 não requer uma licença.
Peter Lawrey
5

Recentemente, fui transferido de

CMS para G1GC com heap 4G e processador de 8 núcleos em servidores com JDK 1.7.45 .

(JDK 1.8.x G1GC é preferível a 1.7, mas devido a algumas limitações, tenho que me ater à versão 1.7.45)

Eu configurei os parâmetros-chave abaixo e mantive todos os outros parâmetros com os valores padrão.

-XX:G1HeapRegionSize=n, XX:MaxGCPauseMillis=m, -XX:ParallelGCThreads=n, 
-XX:ConcGCThreads=n apart from -Xms and -Xmx

Se você quiser ajustar esses parâmetros, dê uma olhada neste artigo do oracle .

Observações principais:

  1. O uso de memória é consistente com G1GC, ao contrário de alto e baixo com CMS
  2. O tempo máximo de pausa do GC é menor em comparação ao CMS
  3. O tempo gasto na coleta de lixo é um pouco alto no G1GC em comparação com o CMS.
  4. O número de coleções principais é quase insignificante em comparação com o CMS
  5. O número de coleções menores é superior em comparação ao CMS

Mas ainda estou feliz que o tempo de pausa Max GC é menor do que no CMS. Eu defini o tempo máximo de pausa do GC como 1,5 segundos e este valor ainda não foi ultrapassado.

Pergunta relacionada de SE:

Coleta de lixo Java 7 (JDK 7) e documentação no G1

Ravindra babu
fonte
4

O CMS pode levar a uma degradação lenta do desempenho, mesmo se você o estiver executando sem acumular objetos titulados. Isso ocorre por causa da fragmentação da memória que G1 supostamente evita.

O mito do G1 disponível apenas com suporte pago é apenas isso, um mito. A Sun e agora a Oracle esclareceram isso na página JDK.

Ted Dunning
fonte
4

O G1 GC deve funcionar melhor. Mas se definir -XX: MaxGCPauseMill for muito agressivo, o lixo será coletado muito lentamente. E é por isso que o GC completo foi acionado no exemplo de David Leppik.

Hypheng
fonte
4

Acabei de implementar o G1 Garbage Collector em nosso projeto Terracotta Big Memory. Trabalhando em diferentes tipos de coletores, G1 nos deu os melhores resultados com menos de 600ms de tempo de resposta.

Você pode encontrar os resultados do teste (26 no total) aqui

Espero que ajude.

Espanglês
fonte
3

Migrei recentemente parte do Twicsy para um novo servidor com 128 GB de RAM e decidi usar 1.7. Comecei usando todas as mesmas configurações de memória que usei no 1.6 (tenho várias instâncias executando fazendo várias coisas, em qualquer lugar de 500 MB de heap até 15 GB, e agora um novo com 40 GB) e isso não funcionou bem . 1.7 parece usar mais heap do que 1.6 e tive muitos problemas nos primeiros dias. Felizmente, eu tinha bastante RAM para trabalhar e aumentei a RAM para a maioria dos meus processos, mas ainda estava tendo alguns problemas. Meu MO normal era usar um tamanho de heap mínimo muito pequeno de 16 m, mesmo com um heap máximo de vários gigabytes, e então ligar o GC incremental. Isso manteve as pausas mínimas. Isso não funciona agora, e eu tive que aumentar o tamanho mínimo para o que eu esperava usar em média na pilha, e isso funcionou muito bem. Ainda tenho o GC incremental ativado, mas tentarei sem ele. Sem nenhuma pausa agora, e as coisas parecem estar correndo muito rápido. Então, acho que a moral da história é não esperar que suas configurações de memória sejam traduzidas perfeitamente de 1.6 para 1.7.

Chris Seline
fonte
2

G1 torna o aplicativo muito mais ágil: a latência do aplicativo aumentará - o aplicativo pode ser chamado de "soft-real-time". Isso é feito substituindo-se dois tipos de execuções de GC (pequenas, menores e uma grande na Geração Tenured) por outras de tamanho igual.

Para mais detalhes veja isto: http://geekroom.de/java/java-expertise-g1-fur-java-7/

Daniel
fonte
1

Estou trabalhando com Java, para Heap pequeno e grande, e a questão do GC e do GC Completo aparece todos os dias, pois as restrições podem ser mais rígidas do que outras: em determinado ambiente, 0,1 segundo de GC scavenger ou GC Completo, kill simplesmente a fonctionnalité, e ter configuração granulada fina e capacidade é importante (CMS, iCMS, outros ... o objetivo está aqui para ter o melhor tempo de resposta possível com o tratamento quase em tempo real (aqui o tratamento em tempo real é frequentemente de 25 ms) , portanto, basicamente, quaisquer melhorias na ergonomia e heuristique do GC são bem-vindas!

Fuby
fonte
1

Eu uso G1GC no Java 8 e também com Groovy (também Java 8), e estou fazendo vários tipos de cargas de trabalho e, em geral, G1GC funciona assim:

  • O uso de memória é muito baixo, por exemplo, 100 MB em vez de 500 MB em comparação com as configurações padrão do Java

  • O tempo de resposta é consistente e muito baixo

  • O desempenho entre as configurações padrão e G1GC é 20% mais lento ao usar G1GC no pior cenário (sem ajuste, aplicativo de thread único). Não é muito considerando o bom tempo de resposta e o baixo uso de memória.

  • Ao executar a partir do Tomcat, que é multithread, o desempenho geral é 30% melhor e o uso de memória é muito menor, assim como os tempos de resposta são muito menores.

Portanto, em geral, ao usar cargas de trabalho realmente diversas, G1GC é um coletor muito bom para Java 8 para aplicativos multi-threaded e, mesmo para single-threaded, há alguns benefícios.

Andrew
fonte
0

Não é sugerido usar java8 com G1GC para cálculo de ponto flutuante com JVM semelhante a ponto de acesso. É perigoso para a integridade e precisão do aplicativo.

https://bugs.openjdk.java.net/browse/JDK-8148175

JDK-8165766

JDK-8186112

user7796409
fonte