JVM de fuga de memória não heap

3

Eu tenho um glassfish v4.0 configurado no servidor Ubuntu em execução na máquina virtual oracle java e o tamanho da memória residente do processo jvm (obtido via comando "top") cresce até o jvm não ter memória para criar um novo thread.

O que eu tenho:

  • Servidor VPS com 1 GB de RAM e processador de 1.4 GHz (1Core)
  • Ubuntu Server 12.04
  • Ambiente de tempo de execução Java (TM) SE (build 1.7.0_51-b13)
  • VM do servidor Java HotSpot (TM) de 64 bits (compilação 24,51-b03, modo misto)
  • Glassfish v4.0 executando minha aplicação web Java EE
  • VM é executada com parâmetros de folowwing -XX: MaxPermSize = 200 m -XX: PermSize = 100 m -XX: Xmx = 512m (posso adicionar tudo se for relevante)

Qual é o problema:

O uso de RAM (memória res) aumenta o tempo todo, dependendo do uso de 10 a 100m por hora, até que o jvm não possa alocar memória nativa.

O que tentei:

  • Baixei o espaço de heap máximo, o que economiza tempo até que o jvm trave de qualquer maneira
  • Anexei o plumbr ( link ) que não detecta vazamento de memória no heap
  • Também defini o tamanho máximo do perm para um valor menor.

Eu gostaria que minha JVM ficasse estável, já que eu meço o espaço de heap + perm gen leva apenas 400-600 mb enquanto o comando "top" mostra a memória de processo java cresce até 850mb e depois se mata. Eu sei que a JVM precisa de mais memória que permita espaço e heap, mas você acha que eu ainda tenho dado muita memória para acumular espaço e perm gen? Qualquer ajuda ou guia será muito apreciado.

Saída de log: link Todos os flgas da JVM: link

Atualizar

O que mais eu tentei (com base em sugestões e minhas próprias descobertas):

  • Eu reduzi e fixei o espaço do heap para 256m e depois diminuí o tempo enquanto o sistema ainda está estável, notei que o heap máximo que eu posso ter no meu sistema é de 512m e 128m de espaço permanente. (-Xmx512m, -Xms512m, -XX: PermSize = 128m, -XX: MaxPermSize = 128m)
  • Tamanho do encadeamento de java reduzido -Xss256k, não consegui reduzi-lo a menos de 218k (o jvm não inicia)
  • Adicionado -D64 para que o jvm seja executado no modo de 64 bits
  • Adicionado -XX: + AggressiveOpts (para ativar a otimização de desempenho), -XX: + UseCompressedOops (para reduzir o uso de memória de espaço de heap), -server flag para iniciar o jvm no modo de servidor
  • Como eu tenho tamanho de heap muito limitado modifiquei NewRatio para ter uma geração tenured um pouco maior (1/3 do espaço de heap) -XX: NewRatio = 3
  • Opções de diagnóstico adicionadas para GC para que eu possa inspecionar erros do OOM -XX: + PrintTenuringDistribution -XX: + PrintGCDetails -XX: + PrintGCTimeStamps -XX: + HeapDumpOnOutOfMemoryError -Xloggc: /home/myuser/garbage.log

Status atual Com essas mudanças, finalmente limpei a memória residente (uso de RAM) para o processo java, que era o meu alvo. No meu caso, 512m de espaço de heap + 128m perm gen space resultam em cerca de 750m de memória residente do processo java que é estável. Mesmo que eu ainda tenha problemas de memória - a memória heap fica cheia de tempos em tempos e faz com que o aplicativo da web congele devido à coleta de lixo contínua, mas o sistema operacional não mata o processo. Então eu preciso agora aumentar a memória disponível (RAM) para o sistema ou inspecionar o uso do heap e diminuir o footprint do meu aplicativo. Como minha webapp é baseada em Java EE (com EJB), talvez não seja possível reduzi-la significativamente. De qualquer forma, obrigado por sugestões e fique à vontade para compartilhar quaisquer outras sugestões, se houver.

    
por Oskars Pakers 31.03.2014 / 14:17

2 respostas

3

Existem algumas possibilidades dadas ao que você compartilhou, por exemplo:

  • uma biblioteca JNI com vazamento ou
  • um vazamento de criação de thread ou
  • proxies de código dinâmico com vazamento (perm-gen leak),

mas só posso adivinhar porque você não forneceu nenhuma saída de log ou indique se a JVM estava lançando um OutOfMemoryException (OOM) ou se alguma outra falha foi encontrada. Nem você mencionou o que o coletor de lixo estava em uso, embora as flags mostradas acima sejam as únicas opções da JVM em uso, é o coletor do CMS.

O primeiro passo é tornar as ações do coletor de lixo observáveis, adicionando estes sinalizadores:

-XX:+PrintTenuringDistribution
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+HeapDumpOnOutOfMemoryError
-Xloggc:/path/to/garbage.log

Se realmente é um OOM, você pode analisar o despejo de heap com VisualVM ou ferramenta semelhante. Eu também uso o VisualVM para monitorar a ação do GC in-situ via JMX. A visibilidade para os internos da JVM via pode ser ativada por esses flags da JVM:

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=4231
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

Recursos adicionais:

Atualizar

O log realmente ajuda. Obrigado. Esse log específico mostra que ficou sem memória física antes que ele pudesse aumentar o heap para o máximo configurado. Tentou malloc ~ 77M e havia apenas ~ 63M de esquerda:

Native memory allocation (malloc) failed to allocate 77873152 bytes for committing reserved memory.

..

/proc/meminfo: MemTotal: 1018724 kB MemFree: 63048 kB

Veja o que eu faria:

  1. Reduza o heap para que ele "caiba" na máquina. Configure min e max heap para o mesmo valor você pode dizer se vai se encaixar imediatamente - não será iniciado se não se encaixa.

  2. Você pode reduzir o tamanho da pilha Java ( -Xss ), mas essa coisa não parece estar fazendo um monte de tópicos para a poupança não será mais do que um Mb ou dois. Eu acho que o padrão para o Linux de 64 bits é de 256k. Reduza demais e comece a OOM nas alocações de pilha.

  3. Repita o teste.

  4. Quando estiver sendo executado sob carga por um curto período, produza despejo de heap sob demanda para diagnóstico diferencial usando   %código%.

  5. Uma das duas coisas deve acontecer: (a) se houver um vazamento, ele falhará novamente, mas o tipo de OOM deve ser diferente, ou (b) não há um vazamento de tal forma que GC irá apenas trabalhe mais e você está feito. Como você tentou isso antes, o primeiro caso é provável, a menos que seu tamanho máximo reduzido também não se encaixe.

  6. Se fizer OOM, compare os dois dumps para ver o que cresceu usando jmap -dump:file=path_to_file <pid> ou algum outro analisador de heap.

Boa sorte!

    
por 31.03.2014 / 20:02
0

Tente executar o processo no modo de 64 bits adicionando: -D64 aos sinalizadores de inicialização da JVM.

Você pode executar pmap $JVMPID para ver como a memória virtual é alocada. Execute-o antes que ele trave.

    
por 01.04.2014 / 08:26