O que consome memória no processo java?

18

estamos tentando investigar o uso de memória do processo java sob 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 = 512 m
  • -XX: MaxDirectMemorySize = 256m
  • ... alguns outros para o GC e outras coisas

Olhando para essas configurações e para o uso real da memória, estamos tropeçando para ver a diferença do que esperamos que este processo esteja usando e o que ele realmente usa.

Normalmente, nossos problemas de memória são resolvidos analisando o despejo de heap, 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 esse seja um problema relacionado a heap, pois ainda temos muito espaço:

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 certa quantidade 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 quais são os pedaços de 64Mb? O que os está usando? Que tipo de dados está neles?

Obrigado

    
por Konstantin S. 16.12.2011 / 12:27

6 respostas

21

O problema pode estar relacionado a este problema da glibc .

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

Em Java, onde você faz o polimento com threads, isso pode levar rapidamente a muitas arenas sendo criadas. E há alocações espalhadas por todas essas arenas. Inicialmente, cada arena de 64Mb é mapeada apenas para a memória sem uso, mas à medida que você faz as alocações, você começa a usar a memória real para elas.

Seu pmap provavelmente tem 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 a soluções alternativas : O bug menciona alguns parâmetros de ambiente que você pode definir para limitar o número de arenas, mas você precisa de uma versão glibc alta o suficiente.

    
por 13.11.2013 / 14:32
3

Que tal Lamdba Probe ? Entre outras coisas, pode mostrar as quebras de uso de memória semelhantes à captura de tela abaixo:

Às vezes, pmap -x your_java_pid também pode ser útil.

    
por 16.12.2011 / 12:35
2

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

    
por 19.12.2011 / 17:08
2

O problema está fora do heap, então 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.

    
por 18.07.2013 / 15:57
1

Existe uma ferramenta útil para visualizar a alocação da memória heap incluída no JDK chamado jmap. Além disso, você também tem stack 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.

    
por 16.12.2011 / 12:50
0

Use o JVisualVM. Ele tem várias visões diferentes que lhe dirão quanto memória heap está em uso, PermGen e assim por diante.

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

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

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

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

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

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

    
por 23.01.2013 / 16:52