web-dev-qa-db-fra.com

Qu'est-ce qui consomme la mémoire dans Java processus?

nous essayons d'enquêter sur l'utilisation de la mémoire Java processus sous 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 constater, nous avons une mémoire résidue à 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 = 256m
  • ... quelques autres pour GC et Stuff

En regardant ces paramètres et à une utilisation réelle de la mémoire, nous sommes trébuchés pour voir la différence de ce que nous espérons que ce processus utilise et ce qu'il utilise réellement.

Habituellement, nos problèmes de mémoire sont résolus en analysant la décharge de tas, mais dans ce cas, notre mémoire est utilisée quelque part à l'extérieur du tas.

Questions: Quelles seraient les étapes pour essayer de trouver la raison d'une combinaison de mémoire aussi élevée? Quels outils pourraient nous aider à identifier ce qui utilise la mémoire dans ce processus?

éditer

Cela ne semble pas que ceci est un problème de démarrage car nous avons toujours un peu d'espace là-bas:

jmap -heap 12663

résultats (é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

éditer 1

en utilisant le PMAP, nous pouvons voir qu'il y a beaucoup 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? Quel type de données est en eux?

Merci

20
Konstantin S.

La question peut être liée à celle-ci NUMÉRO GLIBC .

Fondamentalement, lorsque vous avez plusieurs threads allouant la mémoire, GLIBC augmentera le nombre d'arènes disponibles pour la répartition de l'affectation pour éviter la conflit de verrouillage. Une arène est de 64 Mo de grande taille. La limite supérieure consiste à créer 8 fois le numréber des noyaux arènes. Des arènes seront créées sur demande lorsqu'un fil accédant à une arène déjà verrouillée de sorte qu'il s'agrure avec le temps.

IN Java Où vous saupoudrez de threads, cela peut rapidement conduire à de nombreuses arènes créées. Et il y a des allocations réparties sur ces arènes. Au début, chaque arène de 64 Mo est juste mappée de mémoire non capturée, mais Comme vous faites des allocations, vous commencez à utiliser la mémoire réelle pour eux.

Votre PMAP a probablement des listes similaires à celles-ci ci-dessous. Remarquez comment 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. C'est-à-dire qu'ils résument jusqu'à 64 Mo.

[.____] 00007F4394000000 324K RW --- [anon] [.____] 00007f4394051000 65212K ----- [anon] [.____] 00007f4398000000 560K RW --- [anon] [.____] [. ____] 00007F439808C000 64976K ----- [anon] [.____] 00007f439c000000 620k rw --- [anon] [.____] 00007f439c09b000 64916k ----- [anon] [.____]

Quant à Solutions de contournement : Le bogue mentionnez 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 haut.

22
Christian

Le problème est en dehors du tas, le meilleur candidat est le meilleur candidat:

JNI leak  
Allocation of direct memory buffer

En raison du fait que vous avez limité la taille du tampon direct, le meilleur candidat à mon avis est une fuite JNI.

2
Venceslas

JProfiler pourrait être quelque chose que vous recherchez mais ce n'est pas gratuit. Un autre bon et gratuit outil d'enquête sur l'utilisation de la mémoire de Java processus est Java Visualvm disponible en tant qu'outil JDK dans les distributions Oracle/Sun JDK. J'aurais personnellement Recommandez une approche plus holistique du problème (à savoir surveiller les disques JDK + OS +, etc.) - Utilisation d'un système de surveillance de réseau - Nagios, Verax NMS ou Openns.

2
xantross

Il existe un outil pratique pour afficher l'attribution de la mémoire de tas incluse dans la JDK appelée JMAP, en haut de votre part, 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:

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

Pour obtenir plus encore plus d'informations, vous pouvez vous connecter au processus avec JConsole (également inclus dans le JDK). JConsole a toutefois réplique JMX à configurer dans l'application.

1
HampusLi

Utilisez Jvisalvm. Il offre diverses vues différentes qui vous diront à quel point la mémoire de tas est utilisée, PermGen et ainsi de suite.

Pour répondre à votre question. Java gère la mémoire de manière assez différente de ce que vous pourriez attendre.

Lorsque vous définissez les paramètres -XMS et -XMX, vous indiquez à la JVM quelle quantité de mémoire il devrait allouer dans le tas de commencer et à quel point il devrait également allouer au maximum.

Si vous avez un Java application qui a utilisé un total de 1 m de mémoire mais transmis -xms256m -xmx2g, la JVM s'initialisera avec 256 m de mémoire utilisée. Il n'utilisera pas moins que cela. Peu importe que votre application utilise seulement 1 million de mémoire.

Deuxièmement. Dans ce cas, si vous utilisez une application à un moment donné, utilise plus de 256 m de mémoire, la JVM allouera autant de mémoire que nécessaire pour servir la demande. Cependant, il ne sera pas Déglage la taille du tas à la valeur minimale. Au moins, pas dans la plupart des cas.

Dans votre cas, puisque vous définissez un minimum et une mémoire maximale à 2G, le JVM allouera 2G au début et le maintenir.

La gestion de la mémoire Java est assez complexe et l'utilisation de la mémoire de syntonisation peut être une tâche en soi. Il y a cependant beaucoup de ressources qui peuvent aider.

0
drone.ah