web-dev-qa-db-fra.com

Surveiller l'utilisation de la mémoire non-tas d'une machine virtuelle

Nous traitons généralement les problèmes de OutOfMemoryError à cause d'un problème de configuration de tas ou de taille permgen.

Mais toute la mémoire de la machine virtuelle Java n'est ni permgen ni heap . Pour autant que je sache, elle peut également être liée à Threads/Stacks, code JVM natif ...

Mais avec pmap, je peux voir que le processus est alloué avec 9.3G, ce qui correspond à 3,3G d’utilisation de mémoire off-tas.

Je me demande quelles sont les possibilités de surveiller et d’ajuster cette consommation supplémentaire de mémoire. 

Je n'utilise pas d'accès direct à la mémoire off-tas (MaxDirectMemorySize est 64m par défaut)

Context: Load testing
Application: Solr/Lucene server
OS: Ubuntu
Thread count: 700
Virtualization: vSphere (run by us, no external hosting)

JVM

Java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

Tunning

-Xms=6g
-Xms=6g
-XX:MaxPermSize=128m

-XX:-UseGCOverheadLimit
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSClassUnloadingEnabled

-XX:+OptimizeStringConcat
-XX:+UseCompressedStrings 
-XX:+UseStringCache 

Cartes mémoire:

https://Gist.github.com/slorber/5629214

vmstat

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 1  0   1743    381      4   1150    1    1    60    92    2    0  1  0 99  0

libre

             total       used       free     shared    buffers     cached
Mem:          7986       7605        381          0          4       1150
-/+ buffers/cache:       6449       1536
Swap:         4091       1743       2348

Haut

top - 11:15:49 up 42 days,  1:34,  2 users,  load average: 1.44, 2.11, 2.46
Tasks: 104 total,   1 running, 103 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.5%us,  0.2%sy,  0.0%ni, 98.9%id,  0.4%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8178412k total,  7773356k used,   405056k free,     4200k buffers
Swap:  4190204k total,  1796368k used,  2393836k free,  1179380k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                                 
17833 jmxtrans  20   0 2458m 145m 2488 S    1  1.8 206:56.06 Java                                                                                                                                    
 1237 logstash  20   0 2503m 142m 2468 S    1  1.8 354:23.19 Java                                                                                                                                    
11348 Tomcat    20   0 9184m 5.6g 2808 S    1 71.3 642:25.41 Java                                                                                                                                    
    1 root      20   0 24324 1188  656 S    0  0.0   0:01.52 init                                                                                                                                    
    2 root      20   0     0    0    0 S    0  0.0   0:00.26 kthreadd             
...

df -> tmpfs

Filesystem                1K-blocks     Used Available Use% Mounted on
tmpfs                       1635684      272   1635412   1% /run

Le problème principal que nous avons:

  • Le serveur a 8G de mémoire physique
  • Le tas de Solr ne prend que 6G
  • Il y a 1.5G de swap
  • Swappiness = 0
  • La consommation de tas semble bien adaptée
  • En cours d'exécution sur le serveur: uniquement Solr et quelques éléments de surveillance
  • Nous avons un temps de réponse moyen correct
  • Nous avons parfois des pauses anormalement longues, jusqu'à 20 secondes

Je suppose que les pauses pourraient être un GC complet sur un tas échangé non?

Pourquoi y a-t-il tant d'échanges?

Je ne sais même pas vraiment si c'est la JVM qui échange le serveur ou si c'est quelque chose de caché que je ne peux pas voir. Peut-être le cache de la page du système d'exploitation? Mais je ne sais pas pourquoi le système d'exploitation créerait des entrées de cache de page si cela crée un échange.

J'envisage de tester l'astuce mlockall utilisée dans certains systèmes de stockage/NoSQL Java tels que ElasticSearch, Voldemort ou Cassandra: check Ne permutez pas la JVM/Solr, à l'aide de mlockall


Modifier:

Ici vous pouvez voir max tas, tas utilisé (bleu), un swap utilisé (rouge). Cela semble un peu lié.

Swap and Heap

Je peux voir avec Graphite qu'il y a beaucoup de ParNew GC se produisant régulièrement. Et il y a quelques CMS CMS qui correspondent à la décroissance de tas significative de l'image.

Les pauses ne semblent pas être corrélées à la diminution du tas, mais sont régulièrement réparties entre 10h00 et 11h30. Cela pourrait donc être lié au ParNew GC, je suppose.

Pendant le test de charge, je peux voir une activité du disque ainsi qu'une activité d'échange IO qui est vraiment calme lorsque le test se termine.

29
Sebastien Lorber

Votre segment utilise actuellement 6,5 Go de mémoire virtuelle (ceci peut inclure la perm gen)

Vous avez un tas de threads utilisant des piles de 64 Mo. Pas clair pourquoi certains sont et d'autres utilisent la valeur par défaut de 1 Mo.

Le total est de 9,3 millions de Ko de mémoire virtuelle. Je ne m'inquiéterais que de la taille des résidents.

Essayez d'utiliser top pour trouver la taille de résident du processus.

Vous pouvez trouver ce programme utile

    BufferedReader br = new BufferedReader(new FileReader("C:/dev/gistfile1.txt"));
    long total = 0;
    for(String line; (line = br.readLine())!= null;) {
        String[] parts = line.split("[- ]");
        long start = new BigInteger(parts[0], 16).longValue();
        long end = new BigInteger(parts[1], 16).longValue();
        long size = end - start + 1;
        if (size > 1000000)
            System.out.printf("%,d : %s%n", size, line);
        total += size;
    }
    System.out.println("total: " + total/1024);

Sauf si vous avez une bibliothèque JNI utilisant la mémoire, je suppose que vous avez beaucoup de threads qui ont chacun leur propre espace de pile. Je voudrais vérifier le nombre de threads que vous avez. Vous pouvez réduire l'espace de pile maximal par thread, mais une meilleure option pourrait être de réduire le nombre de threads que vous avez.

La mémoire off-tas est par définition non gérée, elle n'est donc pas facilement "réglée" en tant que telle. Même régler le tas n'est pas simple.

La taille de pile par défaut sur les machines virtuelles Java 64 bits est de 1024 Ko, de sorte que 700 threads utiliseront 700 Mo de mémoire virtuelle.

Vous ne devez pas confondre les tailles de mémoire virtuelle avec les tailles de mémoire résidentes. La mémoire virtuelle sur une application 64 bits est presque gratuite et vous devez vous préoccuper uniquement de la taille de la taille du résident.

Selon moi, vous avez 9,3 Go au total.

  • Tas de 6,0 Go.
  • 128 Mo permanent
  • 700 Mo de piles.
  • <250 bibliothèques partagées
  • 2,2 Go d’inconnu (je soupçonne que la mémoire virtuelle n’est pas une mémoire résidente)

La dernière fois que quelqu'un a eu ce problème, il y avait beaucoup plus de threads que prévu. Je voudrais vérifier le nombre maximal de threads que vous avez eu car c'est le pic qui détermine la taille virtuelle. par exemple. était-ce plus proche de 3000?


Hmmm chacune de ces paires est un fil.

7f0cffddf000-7f0cffedd000 rw-p 00000000 00:00 0 
7f0cffedd000-7f0cffee0000 ---p 00000000 00:00 0

et ceux-ci suggèrent que vous avez un peu moins de 700 fils maintenant .....

9
Peter Lawrey

VisualVM constitue un moyen très pratique de surveiller (et de modifier partiellement) les paramètres d'exécution d'une instance JVM:

PS
(supprimé)

PPS Je me suis souvenu de l’autre outil que j’avais utilisé il ya quelque temps: Visual GC . Il vous montre visuellement en détail ce qui se passe dans la gestion de la mémoire de la machine virtuelle Java, ici quelques captures d'écran . Très puissant, il peut même être intégré à un plugin dans VisualVM (voir la section plugins sur la page d'accueil VisualVM).

PPPS
We sometimes have anormaly long pauses, up to 20 seconds. [...] I guess the pauses could be a full GC on a swapped heap right?
.__ Oui, ça pourrait être. Ces longues pauses peuvent être provoquées par un GC complet, même sur des tas non échangés. Avec VisualVM, vous pouvez surveiller si un CPG complet se produit au moment où la pause de ~ 20 secondes se produit. Je suggère d'exécuter VisualVM sur un autre hôte et de le connecter au processus JVM de votre serveur virtuel via JMX explicite , afin de ne pas fausser les mesures avec une charge supplémentaire. Vous pouvez laisser cette configuration s’étaler sur plusieurs jours/semaines et rassembler des informations définitives sur le phénomène.

Afaics avec des informations actuelles, pour le moment il n'y a que ces possibilités:

  • les pauses observées se produisent simultanément avec le CPG complet: la JVM n’est pas correctement réglée. Vous pouvez résoudre ce problème via les paramètres de la machine virtuelle Java, et peut-être en choisissant un autre algorithme/moteur GC (avez-vous essayé CMS et G1 GC? Plus d'informations sur la procédure, par exemple ici
  • les pauses observées ne correspondent pas à un CPG complet dans la machine virtuelle: l'hôte virtuel physique peut en être la cause. Vérifiez vos SLA (la quantité de mémoire virtuelle RAM garantie dans la RAM physique) et contactez votre prestataire de services pour lui demander de surveiller le serveur virtuel. 

J'aurais dû mentionner que VisualVM est livré avec Java. Et JConsole, également livré avec Java, qui est plus léger et compact que VisualVM (mais n’a pas de plug-ins, pas de profilage, etc.), mais fournit un aperçu similaire.

Si la configuration de la connexion JMX pour VisualVM/JConsole/VisualGC est trop compliquée pour le moment, vous pouvez recourir aux paramètres Java suivants: -XX:+PrintGC -XX:+PrintGCTimeStamps -Xloggc:/my/log/path/gclogfile.log. Ces paramètres obligeront la machine virtuelle Java à écrire dans le fichier journal spécifié une entrée pour chaque exécution du GC. Cette option est également bien adaptée aux analyses à long terme et est probablement celle qui génère le moins de temps système sur votre JVM.

Après avoir repensé (et répété) à votre question: si vous vous demandez d’où proviennent les 3 Go supplémentaires, voici un question connexe . Personnellement, j'utilise le facteur x1.5 comme règle générale.

1
t0r0X

En utilisant jps et jstat, vous pouvez simplement suivre les détails de la mémoire de votre programme Java.

Recherchez le pid à l'aide de la commande jps et utilisez ce pid pour obtenir les détails de la mémoire du processus Java souhaité à l'aide de jstat $pid. Si nécessaire, lancez-les en boucle et vous pourrez surveiller de près les détails de la mémoire que vous souhaitez.

Vous pouvez trouver une implémentation bash de cette idée sur github

0
amarjeetAnand