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
Respostas:
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.
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.
fonte
Como cerca de Lamdba Probe ? Entre outras coisas, ele pode mostrar falhas de uso de memória semelhantes à captura de tela abaixo:
Às vezes
pmap -x your_java_pid
também pode ser útil.fonte
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.
fonte
O problema está fora do heap, portanto, o melhor candidato é:
Devido ao fato de você ter limitado o tamanho do buffer direto, o melhor candidato na minha opinião é o vazamento de JNI.
fonte
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:
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.
fonte
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.
fonte