Nous avons une version bêta ouverte d'une application qui provoque parfois un débordement de l'espace de stockage. La JVM réagit en partant en vacances permanentes.
Pour analyser cela, j'aimerais jeter un coup d'œil dans la mémoire au point où cela a échoué. Java ne veut pas que je fasse cela. Le processus est toujours en mémoire, mais il ne semble pas être reconnu comme un processus Java.
Le serveur en question est un serveur Debian Lenny, Java 6u14
/opt/jdk/bin# ./jmap -F -dump:format=b,file=/tmp/apidump.hprof 11175
Attaching to process ID 11175, please wait...
Sun.jvm.hotspot.debugger.NoSuchSymbolException: Could not find symbol "gHotSpotVMTypeEntryTypeNameOffset" in any of the known library names (libjvm.so, libjvm_g.so, gamma_g)
at Sun.jvm.hotspot.HotSpotTypeDataBase.lookupInProcess(HotSpotTypeDataBase.Java:390)
at Sun.jvm.hotspot.HotSpotTypeDataBase.getLongValueFromProcess(HotSpotTypeDataBase.Java:371)
at Sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.Java:102)
at Sun.jvm.hotspot.HotSpotTypeDataBase.<init>(HotSpotTypeDataBase.Java:85)
at Sun.jvm.hotspot.bugspot.BugSpotAgent.setupVM(BugSpotAgent.Java:568)
at Sun.jvm.hotspot.bugspot.BugSpotAgent.go(BugSpotAgent.Java:494)
at Sun.jvm.hotspot.bugspot.BugSpotAgent.attach(BugSpotAgent.Java:332)
at Sun.jvm.hotspot.tools.Tool.start(Tool.Java:163)
at Sun.jvm.hotspot.tools.HeapDumper.main(HeapDumper.Java:77)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
at Java.lang.reflect.Method.invoke(Method.Java:597)
at Sun.tools.jmap.JMap.runTool(JMap.Java:179)
at Sun.tools.jmap.JMap.main(JMap.Java:110)
Debugger attached successfully.
Sun.jvm.hotspot.tools.HeapDumper requires a Java VM process/core!
La solution était très simple. J'exécutais le jmap en tant que root, mais je devais l'exécuter en tant qu'utilisateur ayant lancé le jvm. Je vais maintenant aller cacher ma tête dans la honte.
J'exécutais le jmap et l'application avec le même utilisateur et reçois toujours l'erreur.
La solution a été lancée avant le jmap
echo 0 | Sudo tee /proc/sys/kernel/yama/ptrace_scope
Than n’utilise que jmap et fonctionne correctement
jmap -heap 17210
Futurs Googlers:
Cela pourrait également se produire si vous avez installé le JDK alors que le processus que vous essayez d'exécuter jmap était en cours d'exécution.
Si tel est le cas, redémarrez le processus Java.
Si quelqu'un essaie de télécharger Heap Dump d'une application Java dans le conteneur Docker, c'est la seule solution qui fonctionne pour moi:
docker exec <container-name> jcmd 1 GC.heap_dump /tmp/docker.hprof
Il vide essentiellement le tas de processus avec pid = 1 en utilisant jcmd
Voir https://docs.Oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr006.html
Qu'est-ce qui se passe si vous venez de courir
./jmap -heap 11175
Et êtes-vous sûr que la machine virtuelle d'application est identique à la machine virtuelle JMAP? (même version, etc)
Vous devez utiliser le jmap fourni avec la machine virtuelle Java.
Suivez les étapes ci-dessous pour récupérer le thread et le vidage de segments de mémoire d'un conteneur de menu fixe.
docker exec -it CONTAINER_NAME bash
jps
Ensuite, exécutez la commande ci-dessous pour obtenir le vidage de thread. Veuillez modifier le PID de manière appropriée
jstack PID > threadDump.tdump
Ensuite, exécutez la commande ci-dessous pour obtenir le vidage de tas. Veuillez modifier le PID de manière appropriée
jmap -dump:live,format=b,file=heapDump.hprof PID
Sudo docker cp CONTAINER_NAME:threadDump.tdump . Sudo docker cp CONTAINER_NAME:heapDump.hprof .
J'ai la même erreur jmap sur une machine Linux sur laquelle deux OpenJdks différents sont installés. J'ai d'abord installé OpenJDK 1.6 et ensuite OpenJDK 1.7.
Un appel de ...
/usr/lib/jvm/Java-1.7.0-openjdk-AMD64/bin/Java -XshowSettings:properties -version
# produce the following output ...
...
Java.library.path = /usr/Java/packages/lib/AMD64
/usr/lib/x86_64-linux-gnu/jni
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/lib/jni
/lib
/usr/lib
...
Java version "1.7.0_65"
Avec l’inclusion de '/ usr/lib' dans OpenJDK 1.7, le programme lancé inclut les bibliothèques du premier JDK installé (dans mon cas OpenJDK 1.6. *). Les versions jmap de Java6 et Java7 ont donc échoué.
Après avoir modifié le début des programmes Java7 avec les bibliothèques OpenJDK 1.7 incluses ...
/usr/lib/jvm/Java-1.7.0-openjdk-AMD64/bin/Java -Djava.library.path=/usr/lib/jvm/Java- \
7-openjdk-AMD64/jre/lib/AMD64/server:/usr/Java/packages/lib/AMD64: \
/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/ \
x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib ...
J'ai pu accéder au processus avec la version Java 7 du programme jmap. Mais il faut un Sudo pour courir.
J'ai le même problème, j'essaie de trouver une fuite de mémoire dans un processus exécuté dans un conteneur Docker. Je n'ai pas pu utiliser jmap, j'ai plutôt utilisé ceci:
jcmd <pid> GC.class_histogram
Cela vous donne une liste des objets dans la mémoire. Et à partir de la documentation Oracle:
Il est recommandé d'utiliser le dernier utilitaire, jcmd, au lieu de l'utilitaire jmap, pour améliorer les diagnostics et réduire les performances. https://docs.Oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks004.html
1.Execute "Docker ps", will give the container Id of all services and collect the container id foe TSC.
2.Execute "docker exec -it CONTAINER_ID bash" (replace CONTAINER_ID with TSC Container id)
3.Bash will come and then execute the "jps" on bash, that will give you the PID for process(it will be 1 for jar)
4.Execute the "jstack PID > threadDump.tdump"(replace PID with process id received in step 3, it should be 1)
5.Execute the "jmap -dump:format=b,file=heapDump.hprof PID"(replace PID with process id received in step 3, it should be 1)
6.Then we have to exit the bash using "exit" command
7.Execute "Sudo docker cp CONTAINER_ID:heapDump.hprof ." from ec2 command line, that will copy the dump file on ec2 machine present working directory.
8.Execute "Sudo docker cp CONTAINER_ID:threadDump.tdump ." from ec2 command line, that will copy the dump file on ec2 machine present working directory.
Ce qui a fonctionné pour moi a été simplement d’émettre la commande avec Sudo comme dans
Sudo jmap -heap 21797
Dans mon cas, ce n'est pas aussi simple que de vérifier l'utilisateur :(
J'ai un script appelé collectd-Java qui appelle jstat et jmap. J'ai vérifié par le haut que ce script est lancé, comme prévu, par l'utilisateur possédant la machine virtuelle Cependant, jstat me donne ce dont j'ai besoin et jmap ne peut pas être attaché. Voici le script - l'écho est le format dont j'ai besoin pour présenter les valeurs:
HOSTNAME="${COLLECTD_HOSTNAME:-localhost}"
INTERVAL="${COLLECTD_INTERVAL:-60}"
MAIN_CLASS="my.fully.qualified.MainClass"
PID=$(pgrep -f ${MAIN_CLASS})
get_jstat_classloaderdata() {
VALUE=`jstat -class $PID 1 1 | awk '{print $1}' | grep -vi loaded`
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-Java_classloader_loaded\" interval=$INTERVAL N:$VALUE"
VALUE=`jstat -class $PID 1 1 | awk '{print $2}' | grep -vi bytes`
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-Java_classloader_bytesload\" interval=$INTERVAL N:$VALUE"
VALUE=`jstat -class $PID 1 1 | awk '{print $3}' | grep -vi unload`
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-Java_classloader_unloaded\" interval=$INTERVAL N:$VALUE"
VALUE=`jstat -class $PID 1 1 | awk '{print $4}' | grep -vi bytes`
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-Java_classloader_bytesunload\" interval=$INTERVAL N:$VALUE"
VALUE=`jstat -class $PID 1 1 | awk '{print $5}' | grep -vi time`
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-Java_classloader_time\" interval=$INTERVAL N:$VALUE"
}
get_jmap_heapdata() {
VALUE=$(jmap -heap ${PID} | grep MinHeapFreeRatio |awk '{print $3}')
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-jmap_minheapfreeratio\" interval=$INTERVAL N:$VALUE"
VALUE=$(jmap -heap ${PID} | grep MaxHeapFreeRatio|awk '{print $3}')
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-jmap_maxheapfreeratio\" interval=$INTERVAL N:$VALUE"
VALUE=$(jmap -heap ${PID} | grep MaxHeapSize|awk '{print $3}')
echo "PUTVAL \"$HOSTNAME/exec-cecoco/gauge-jmap_maxheapsize\" interval=$INTERVAL N:$VALUE"
}
##Do it
get_jmap_heapdata
get_jstat_classloaderdata
Jstat réussit et Jmap échoue. Est-ce que quelqu'un le comprend?
Lorsque rien ne fonctionne ou si vous ne souhaitez pas modifier les indicateurs sensibles du système d'exploitation, tels que ptrace_scope:
Soit vous pouvez utiliser jconsole/jvisualvm pour déclencher des vidages de tas ou exécuter un client JMX directement à partir de la console, comme suit: vous le faites localement sur la machine qui a besoin du vidage et est donc plus rapide:
echo 'jmx_invoke -m com.Sun.management:type=HotSpotDiagnostic dumpHeap heapdump-20160309.hprof false' | Java -jar jmxsh.jar -h $LOCALHOST_OR_IP -p $JMX_PORT
J'ai utilisé le wget https://github.com/davr/jmxsh/raw/master/jmxsh.jar pour cet exemple.
Je ne sais pas pourquoi une "jmap" simple échoue lorsque je docker exec -it
dans mon conteneur exécutant centos7 systemd et un service Java, mais les options ci-dessous de jmap ont fonctionné pour moi. Merci: https://dkbalachandar.wordpress.com/2016/07/05/thread-dump-from-a-docker-container/
[root@b29924306cfe /]# jmap 170
Attaching to process ID 170, please wait...
Error attaching to process: Sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process: ptrace(PTRACE_ATTACH, ..) failed for 170: Operation not permitted
Sun.jvm.hotspot.debugger.DebuggerException: Sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process: ptrace(PTRACE_ATTACH, ..) failed for 170: Operation not permitted
[root@b29924306cfe /]# jmap -dump:live,format=b,file=heapDump.hprof 170
Dumping heap to /heapDump.hprof ...
Heap dump file created