O que consome memória no processo java?

20

estamos tentando investigar o uso da memória do processo java com carga moderada.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Como você pode ver, temos memória residente em 6Gb. Agora a parte interessante é esta: o processo é executado com estes parâmetros:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... alguns outros para GC e outras coisas

Observando essas configurações e o uso real da memória, nos deparamos com a diferença entre o que esperamos que esse processo esteja usando e o que ele realmente usa.

Geralmente, nossos problemas de memória são resolvidos através da análise do heap dump, mas nesse caso nossa memória é usada em algum lugar fora do heap.

Perguntas: Quais seriam as etapas para tentar encontrar o motivo de um uso de memória tão alto? Quais ferramentas podem nos ajudar a identificar o que usa a memória nesse processo?

EDIT 0

Não parece que este seja um problema relacionado à pilha, pois ainda temos bastante espaço lá:

jmap -heap 12663

resulta em (editado para economizar espaço)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

EDIT 1

usando o pmap, podemos ver que há uma quantidade considerável de alocações de 64Mb:

pmap -x 12663 | grep rwx | sort -n -k3 | less

resulta em:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Então, como descobrir o que são esses pedaços de 64 Mb? O que os está usando? Que tipo de dados está neles?

obrigado

Konstantin S.
fonte
2
Eu tenho exatamente o mesmo problema ... aqui está a minha pergunta. stackoverflow.com/questions/18734389/… Você tem uma solução sobre isso?
DeepNightTwo

Respostas:

21

O problema pode estar relacionado a esse problema glibc .

Basicamente, quando você tem vários threads alocando memória, o glibc aumentará o número de arenas disponíveis para alocação, a fim de evitar contenção de bloqueio. Uma arena tem 64Mb de largura. O limite superior é criar 8 vezes o número de arenas de núcleos. As arenas serão criadas sob demanda quando um segmento acessar uma arena que já está bloqueada e aumentar com o tempo.

Em Java, onde você polvilha com threads, isso pode levar rapidamente à criação de muitas arenas. E havendo alocações espalhadas por todas essas arenas. Inicialmente, cada arena de 64Mb é apenas um mapeamento de memória não confirmada, mas à medida que você faz as alocações, você começa a usar a memória real para elas.

Seu pmap provavelmente possui listagens semelhantes a esta abaixo. Observe como 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Ou seja, eles somam 64Mb.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

Quanto às soluções alternativas : O bug menciona alguns parâmetros do ambiente que você pode definir para limitar o número de arenas, mas você precisa de uma versão glibc alta o suficiente.

cristão
fonte
5
configurando Xmx e Xms para o mesmo valor, além de definir a variável de ambiente "export MALLOC_ARENA_MAX = 4" no script sh que inicia nosso serviço da web, que é o nosso caso. Antes disso, o serviço da Web era reiniciado devido ao OOM Killer a cada 2 a 8 horas. Versão glibc no Ubuntu 14.04 é 2.19 o que é bom, já que ele precisa ser> = 2,16 para a definição MALLOC_ARENA_MAX ao trabalho
Kluyg
Esta resposta e o comentário acima foram um salva-vidas para mim. No meu caso, MALLOC_ARENA_MAX = 1 foi necessário e eficaz.
John Bachir
3

Como cerca de Lamdba Probe ? Entre outras coisas, ele pode mostrar falhas de uso de memória semelhantes à captura de tela abaixo:

Visualização de uso de memória do Sonda Lambda

Às vezes pmap -x your_java_pidtambém pode ser útil.

Janne Pikkarainen
fonte
Obrigado pela sua resposta. Se eu entendi corretamente, o Lambda Probe é para o Apache Tomcat? Que não usamos ... Em relação ao pmap, adicionarei as informações ao post principal #
Konstantin S.
2

O JProfiler pode ser algo que você procura, mas não é gratuito. Outra ferramenta boa e gratuita para investigar o uso da memória do processo java é o Java VisualVM, disponível como uma ferramenta JDK nas distribuições Oracle / Sun JDK. Eu pessoalmente recomendaria uma abordagem mais holística do problema (por exemplo, para monitorar discos JDK + OS +, etc) - o uso de algum sistema de monitoramento de rede - Nagios, Verax NMS ou OpenNMS.

xantross
fonte
2
Sua vazamento é off-heap tão JProfiler realmente não vai ajudar aqui
Asaf Mesika
2

O problema está fora do heap, portanto, o melhor candidato é:

JNI leak  
Allocation of direct memory buffer

Devido ao fato de você ter limitado o tamanho do buffer direto, o melhor candidato na minha opinião é o vazamento de JNI.

Venceslas
fonte
1

Existe uma ferramenta útil para visualizar a alocação da memória heap incluída no JDK chamada jmap, além disso você também possui a pilha etc (Xss). Execute estes dois comandos jmap para obter mais informações sobre o uso da memória:

jmap -heap <PID>
jmap -permstat <PID>

Para obter mais informações, você pode se conectar ao processo com o jconsole (também incluído no JDK). O Jconsole, no entanto, exige que o JMX seja configurado no aplicativo.

HampusLi
fonte
Obrigado pela sua resposta, mas o problema parece estar em algum lugar fora da pilha. Eu atualizarei a postagem superior para refletir algumas informações do jmap
Konstantin S.
Quantas threads o processo possui? O tamanho da pilha é 2 MB padrão na maioria das plataformas, portanto multiplique pelo número de threads. Eu ficaria surpreso se ele responder por toda a memória "ausente", mas possivelmente por parte dela.
HampusLi
Cerca de 300 threads, informações obtidas do pmap, temos tamanho de pilha de 1Mb. E a partir da mesma saída do pmap, não parece que nenhuma dessas pilhas use mais do que 100 KB
Konstantin S.
0

Use JVisualVM. Ele tem várias visões diferentes que informam quanta memória heap está sendo usada, PermGen e assim por diante.

Quanto a responder sua pergunta. Java lida com a memória de maneira bem diferente do que você poderia esperar.

Ao configurar os parâmetros -Xms e -Xmx, você está dizendo à JVM quanta memória ela deve alocar no heap para começar e também quanto ela deve alocar no máximo.

Se você tiver um aplicativo java que utilizou um total de 1m de memória, mas passou em -Xms256m -Xmx2g, a JVM se inicializará com 256m de memória usada. Não vai usar menos do que isso. Não importa que seu aplicativo use apenas 1m de memória.

Em segundo lugar. No caso acima, se você aplicar em algum momento mais de 256m de memória, a JVM alocará a quantidade de memória necessária para atender à solicitação. No entanto, ele não reduzirá o tamanho do heap ao valor mínimo. Pelo menos, não na maioria das circunstâncias.

No seu caso, como você está configurando a memória mínima e máxima para 2g, a JVM alocará 2g no início e a manterá.

O gerenciamento de memória Java é bastante complexo e o uso da memória de ajuste pode ser uma tarefa em si. No entanto, existem muitos recursos por aí que podem ajudar.

drone.ah
fonte