nous essayons d'étudier l'utilisation de la mémoire du processus java sous une charge modérée.
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
Comme vous pouvez le voir, nous avons une mémoire résidente à 6 Go. Maintenant, la partie intéressante est la suivante: le processus est exécuté avec ces paramètres:
- -Xmx2048m
- -Xms2048m
- -XX: NewSize = 512m
- -XX: MaxDirectMemorySize = 256 m
- ... quelques autres pour GC et tout ça
En regardant ces paramètres et l'utilisation réelle de la mémoire, nous sommes trébuchés pour voir la différence entre ce que nous attendons de ce processus et ce qu'il utilise réellement.
Habituellement, nos problèmes de mémoire sont résolus en analysant le vidage de tas, mais dans ce cas, notre mémoire est utilisée quelque part en dehors du tas.
Questions: quelles seraient les étapes pour essayer de trouver la raison d'une telle utilisation de la mémoire? Quels outils pourraient nous aider à identifier ce qui utilise la mémoire dans ce processus?
EDIT 0
Il ne semble pas que ce soit un problème lié au tas car nous avons encore beaucoup d'espace là-bas:
jmap -heap 12663
résulte en (édité pour économiser de l'espace)
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
en utilisant le pmap, nous pouvons voir qu'il existe un certain nombre d'allocations de 64 Mo:
pmap -x 12663 | grep rwx | sort -n -k3 | less
résulte en:
... 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 ?
Alors, comment savoir quels sont ces morceaux de 64 Mo? Qu'est-ce qui les utilise? De quel type de données s'agit-il?
Merci
Réponses:
Le problème peut être lié à ce problème de glibc .
Fondamentalement, lorsque vous avez plusieurs threads allouant de la mémoire, la glibc augmentera le nombre d'arènes disponibles pour effectuer l'allocation afin d'éviter les conflits de verrous. Une arène mesure 64 Mo de large. La limite supérieure est de créer 8 fois le nombre d'arènes de noyaux. Les arènes seront créées à la demande lorsqu'un thread accédera à une arène déjà verrouillée afin qu'elle grandisse avec le temps.
En Java où vous saupoudrez de threads, cela peut rapidement conduire à la création de nombreuses arènes. Et il y a des allocations réparties dans toutes ces arènes. Initialement, chaque arène 64 Mo est simplement mappée de mémoire non validée, mais au fur et à mesure que vous effectuez des allocations, vous commencez à utiliser la mémoire réelle pour elles.
Votre pmap a probablement des listes similaires à celle-ci ci-dessous. Remarquez comment 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Autrement dit, ils résument jusqu'à 64 Mo.
En ce qui concerne les solutions de contournement : le bogue mentionne certains paramètres d'environnement que vous pouvez définir pour limiter le nombre d'arènes, mais vous avez besoin d'une version glibc suffisamment élevée.
la source
Qu'en est-il de la sonde Lamdba ? Entre autres choses, il peut vous montrer des pannes d'utilisation de la mémoire similaires à la capture d'écran ci-dessous:
Parfois, cela
pmap -x your_java_pid
peut aussi être utile.la source
JProfiler pourrait être quelque chose que vous recherchez, mais ce n'est pas gratuit. Un autre outil bon et gratuit pour étudier l'utilisation de la mémoire du processus java est Java VisualVM disponible en tant qu'outil JDK dans les distributions Oracle / Sun JDK. Je recommanderais personnellement une approche plus holistique du problème (c'est-à-dire pour surveiller les disques JDK + OS +, etc.) - l'utilisation d'un système de surveillance réseau - Nagios, Verax NMS ou OpenNMS.
la source
Le problème est en dehors du tas, donc les meilleurs candidats sont:
En raison du fait que vous avez limité la taille du tampon direct, le meilleur candidat à mon avis est la fuite JNI.
la source
Il existe un outil pratique pour afficher l'allocation de la mémoire de tas incluse dans le JDK appelée jmap, en plus de cela, vous avez également une pile, etc. (Xss). Exécutez ces deux commandes jmap pour obtenir plus d'informations sur l'utilisation de la mémoire:
Pour obtenir encore plus d'informations, vous pouvez vous connecter au processus avec jconsole (également inclus dans le JDK). Jconsole nécessite cependant que JMX soit configuré dans l'application.
la source
Utilisez JVisualVM. Il a différentes vues différentes qui vous diront combien de mémoire de tas est utilisée, PermGen et ainsi de suite.
Quant à répondre à votre question. Java gère la mémoire très différemment de ce à quoi vous pourriez vous attendre.
Lorsque vous définissez les paramètres -Xms et -Xmx, vous dites à la JVM combien de mémoire elle doit allouer dans le tas pour commencer et aussi combien elle doit allouer au maximum.
Si vous avez une application java qui a utilisé un total de 1 m de mémoire mais qui est passée en -Xms256m -Xmx2g, la JVM s'initialise elle-même avec 256 m de mémoire utilisée. Il n'en utilisera pas moins. Peu importe que votre application utilise seulement 1 m de mémoire.
Deuxièmement. Dans le cas ci-dessus, si votre application utilise à un moment donné plus de 256 m de mémoire, la machine virtuelle Java allouera autant de mémoire que nécessaire pour traiter la demande. Toutefois, il ne ramènera pas la taille du segment de mémoire à la valeur minimale. Du moins, pas dans la plupart des circonstances.
Dans votre cas, étant donné que vous définissez la mémoire minimale et maximale à 2 g, la JVM allouera 2 g au début et la maintiendra.
La gestion de la mémoire Java est assez complexe et l'optimisation de l'utilisation de la mémoire peut être une tâche en soi. Il existe cependant de nombreuses ressources susceptibles de vous aider.
la source