Disons que votre programme Java utilise 100% de CPU. Il a 50 threads. Vous devez trouver quel thread est coupable. Je n'ai pas trouvé d'outil qui peut vous aider. Actuellement, j'utilise le très suivant routine longue:
jstack <pid>
, où pid est l'ID de processus d'un processus Java. Le moyen le plus simple de le trouver est d'exécuter un autre utilitaire inclus dans le JDK - jps
. Il vaut mieux redirige la sortie de jstack vers un fichier.Alternativement, vous pouvez attacher à un processus Java dans Eclipse et essayer de suspendre les threads un par un, jusqu'à ce que vous frappiez celui qui monopolise le processeur. Sur une machine à un processeur, vous devrez peut-être d'abord réduire la priorité du processus Java pour pouvoir se déplacer. Même dans ce cas, Eclipse n'est souvent pas en mesure de se joindre à un processus en cours en raison d'un délai d'attente.
Je m'attendais à ce que l'outil visualvm
de Sun fasse cela.
Quelqu'un connaît-il une meilleure façon?
Essayez de regarder le plugin Hot Thread Detector pour VM virtuelle - il utilise l'API ThreadMXBean pour prendre plusieurs échantillons de consommation CPU pour trouver les threads les plus actifs. Il est basé sur n équivalent en ligne de commande de Bruce Chapman qui pourrait également être utile.
Identifier quel Java Thread consomme le plus de CPU dans le serveur de production.
La plupart (sinon tous) les systèmes productifs faisant quelque chose d'important utiliseront plus de 1 Java thread. Et quand quelque chose devient fou et que votre utilisation du processeur est à 100%, il est difficile d'identifier quel thread ( s) est/sont à l'origine de cela. C'est du moins ce que je pensais. Jusqu'à ce que quelqu'un plus intelligent que moi me montre comment cela peut être fait.
ne application de test
Pour tester cela, nous avons besoin d'une application de test. Je vais donc vous en donner un. Il se compose de 3 classes:
HeavyThread
qui fait quelque chose de gourmand en CPU (calcul des hachages MD5)LightThread
qui fait quelque chose de moins intensif en CPU (comptage et sommeil).StartThreads
pour démarrer 1 processeur intensif et plusieurs threads légers.Voici le code de ces classes:
import Java.security.MessageDigest;
import Java.security.NoSuchAlgorithmException;
import Java.util.UUID;
/**
* thread that does some heavy lifting
*
* @author srasul
*
*/
public class HeavyThread implements Runnable {
private long length;
public HeavyThread(long length) {
this.length = length;
new Thread(this).start();
}
@Override
public void run() {
while (true) {
String data = "";
// make some stuff up
for (int i = 0; i < length; i++) {
data += UUID.randomUUID().toString();
}
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
// hash the data
digest.update(data.getBytes());
}
}
}
import Java.util.Random;
/**
* thread that does little work. just count & sleep
*
* @author srasul
*
*/
public class LightThread implements Runnable {
public LightThread() {
new Thread(this).start();
}
@Override
public void run() {
Long l = 0l;
while(true) {
l++;
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(l == Long.MAX_VALUE) {
l = 0l;
}
}
}
}
/**
* start it all
*
* @author srasul
*
*/
public class StartThreads {
public static void main(String[] args) {
// lets start 1 heavy ...
new HeavyThread(1000);
// ... and 3 light threads
new LightThread();
new LightThread();
new LightThread();
}
}
En supposant que vous n'avez jamais vu ce code, et tout ce que vous avez un PID d'un runaway Java processus qui exécute ces classes et consomme 100% de CPU.
Commençons d'abord par la classe StartThreads
.
$ ls
HeavyThread.Java LightThread.Java StartThreads.Java
$ javac *
$ Java StartThreads &
A ce stade, un processus Java en cours d'exécution devrait prendre 100 cpu. Dans mon top, je vois:
En haut, appuyez sur Shift-H qui active les threads. La page de manuel pour le haut dit:
-H : Threads toggle
Starts top with the last remembered 'H' state reversed. When
this toggle is On, all individual threads will be displayed.
Otherwise, top displays a summation of all threads in a
process.
Et maintenant, dans mon top avec l'affichage des fils activé, je vois:
Et j'ai un processus Java
avec PID 28294
. Permet d'obtenir le vidage de pile de ce processus en utilisant jstack
:
$ jstack 28924
2010-11-18 13:05:41
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0-b16 mixed mode):
"Attach Listener" daemon prio=10 tid=0x0000000040ecb000 nid=0x7150 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" prio=10 tid=0x00007f9a98027800 nid=0x70fd waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Thread-3" prio=10 tid=0x00007f9a98025800 nid=0x710d waiting on condition [0x00007f9a9d543000]
Java.lang.Thread.State: TIMED_WAITING (sleeping)
at Java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.Java:21)
at Java.lang.Thread.run(Thread.Java:619)
"Thread-2" prio=10 tid=0x00007f9a98023800 nid=0x710c waiting on condition [0x00007f9a9d644000]
Java.lang.Thread.State: TIMED_WAITING (sleeping)
at Java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.Java:21)
at Java.lang.Thread.run(Thread.Java:619)
"Thread-1" prio=10 tid=0x00007f9a98021800 nid=0x710b waiting on condition [0x00007f9a9d745000]
Java.lang.Thread.State: TIMED_WAITING (sleeping)
at Java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.Java:21)
at Java.lang.Thread.run(Thread.Java:619)
"Thread-0" prio=10 tid=0x00007f9a98020000 nid=0x710a runnable [0x00007f9a9d846000]
Java.lang.Thread.State: RUNNABLE
at Sun.security.provider.DigestBase.engineReset(DigestBase.Java:139)
at Sun.security.provider.DigestBase.engineUpdate(DigestBase.Java:104)
at Java.security.MessageDigest$Delegate.engineUpdate(MessageDigest.Java:538)
at Java.security.MessageDigest.update(MessageDigest.Java:293)
at Sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.Java:197)
- locked <0x00007f9aa457e400> (a Sun.security.provider.SecureRandom)
at Sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.Java:257)
- locked <0x00007f9aa457e708> (a Java.lang.Object)
at Sun.security.provider.NativePRNG$RandomIO.access$200(NativePRNG.Java:108)
at Sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.Java:97)
at Java.security.SecureRandom.nextBytes(SecureRandom.Java:433)
- locked <0x00007f9aa4582fc8> (a Java.security.SecureRandom)
at Java.util.UUID.randomUUID(UUID.Java:162)
at HeavyThread.run(HeavyThread.Java:27)
at Java.lang.Thread.run(Thread.Java:619)
"Low Memory Detector" daemon prio=10 tid=0x00007f9a98006800 nid=0x7108 runnable [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"CompilerThread1" daemon prio=10 tid=0x00007f9a98004000 nid=0x7107 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"CompilerThread0" daemon prio=10 tid=0x00007f9a98001000 nid=0x7106 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x0000000040de4000 nid=0x7105 runnable [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x0000000040dc4800 nid=0x7104 in Object.wait() [0x00007f9a97ffe000]
Java.lang.Thread.State: WAITING (on object monitor)
at Java.lang.Object.wait(Native Method)
- waiting on <0x00007f9aa45506b0> (a Java.lang.ref.ReferenceQueue$Lock)
at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:118)
- locked <0x00007f9aa45506b0> (a Java.lang.ref.ReferenceQueue$Lock)
at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:134)
at Java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.Java:159)
"Reference Handler" daemon prio=10 tid=0x0000000040dbd000 nid=0x7103 in Object.wait() [0x00007f9a9de92000]
Java.lang.Thread.State: WAITING (on object monitor)
at Java.lang.Object.wait(Native Method)
- waiting on <0x00007f9aa4550318> (a Java.lang.ref.Reference$Lock)
at Java.lang.Object.wait(Object.Java:485)
at Java.lang.ref.Reference$ReferenceHandler.run(Reference.Java:116)
- locked <0x00007f9aa4550318> (a Java.lang.ref.Reference$Lock)
"VM Thread" prio=10 tid=0x0000000040db8800 nid=0x7102 runnable
"GC task thread#0 (ParallelGC)" prio=10 tid=0x0000000040d6e800 nid=0x70fe runnable
"GC task thread#1 (ParallelGC)" prio=10 tid=0x0000000040d70800 nid=0x70ff runnable
"GC task thread#2 (ParallelGC)" prio=10 tid=0x0000000040d72000 nid=0x7100 runnable
"GC task thread#3 (ParallelGC)" prio=10 tid=0x0000000040d74000 nid=0x7101 runnable
"VM Periodic Task Thread" prio=10 tid=0x00007f9a98011800 nid=0x7109 waiting on condition
JNI global references: 910
De mon haut, je vois que le PID du fil supérieur est 28938
. Et 28938
en hexadécimal est 0x710A
. Notez que dans le vidage de pile, chaque thread a un nid
qui est affiché en hexadécimal. Et il se trouve que 0x710A
est l'id du thread:
"Thread-0" prio=10 tid=0x00007f9a98020000 nid=0x710a runnable [0x00007f9a9d846000]
Java.lang.Thread.State: RUNNABLE
at Sun.security.provider.DigestBase.engineReset(DigestBase.Java:139)
at Sun.security.provider.DigestBase.engineUpdate(DigestBase.Java:104)
at Java.security.MessageDigest$Delegate.engineUpdate(MessageDigest.Java:538)
at Java.security.MessageDigest.update(MessageDigest.Java:293)
at Sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.Java:197)
- locked <0x00007f9aa457e400> (a Sun.security.provider.SecureRandom)
at Sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.Java:257)
- locked <0x00007f9aa457e708> (a Java.lang.Object)
at Sun.security.provider.NativePRNG$RandomIO.access$200(NativePRNG.Java:108)
at Sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.Java:97)
at Java.security.SecureRandom.nextBytes(SecureRandom.Java:433)
- locked <0x00007f9aa4582fc8> (a Java.security.SecureRandom)
at Java.util.UUID.randomUUID(UUID.Java:162)
at HeavyThread.run(HeavyThread.Java:27)
at Java.lang.Thread.run(Thread.Java:619)
Et vous pouvez donc confirmer que le thread qui exécute la classe HeavyThread
consomme le plus de CPU.
Dans les situations de lecture, ce sera probablement un tas de threads qui consomment une partie du CPU et ces threads réunis conduiront au processus Java utilisant 100% CPU).
Sommaire
jvmtop peut vous montrer les discussions les plus consommatrices:
TID NAME STATE CPU TOTALCPU
25 http-8080-Processor13 RUNNABLE 4.55% 1.60%
128022 RMI TCP Connection(18)-10.101. RUNNABLE 1.82% 0.02%
36578 http-8080-Processor164 RUNNABLE 0.91% 2.35%
128026 JMX server connection timeout TIMED_WAITING 0.00% 0.00%
Exécutez simplement JVisualVM, connectez-vous à votre application et utilisez la vue des threads. Celui qui reste continuellement actif est votre coupable le plus probable.
Jetez un œil au plugin Top Threads pour JConsole.
Si vous utilisez Windows, essayez Process Explorer . Ouvrez la boîte de dialogue des propriétés de votre processus, puis sélectionnez l'onglet Threads.
Je recommanderais de jeter un œil à Arthas outil open source d'Alibaba.
Il contient un tas de commandes utiles qui peuvent vous aider à déboguer votre code de production:
Utilisez-vous Java 6 sur un ordinateur multicœur?
Les chances sont que vous souffrez d'un problème que je viens de décrire dans un article sur la famine de fil.
Prenez une décharge de fil. Attendez 10 secondes. Prenez un autre vidage de fil. Répétez encore une fois. Inspectez les vidages de threads et voyez quels threads sont bloqués au même endroit ou traitent la même demande. C'est une manière manuelle de le faire, mais souvent utile.
Une option que vous pourriez envisager est d'interroger vos threads pour obtenir la réponse à partir de l'application. Via le ThreadMXBean vous pouvez interroger l'utilisation CPU des threads à partir de votre Java et interroger les traces de pile des threads incriminés).
L'option ThreadMXBean vous permet d'intégrer ce type de surveillance dans votre application en direct. Il a un impact négligeable et présente l'avantage distinct que vous pouvez le faire faire exactement ce que vous voulez.
Si vous pensez que VisualVM est un bon outil, essayez-le (car il le fait). Découvrez que les threads ne vous aident que dans la direction générale de la raison pour laquelle il consomme autant de CPU.
Cependant, si c'est évident, j'irais directement à l'aide d'un profileur pour savoir pourquoi vous consommez autant de CPU.
C'est une sorte de méthode hacky, mais il semble que vous puissiez lancer l'application dans un débogueur, puis suspendre tous les threads, parcourir le code et découvrir lequel ne bloque pas sur un verrou ou un appel d'E/S dans une sorte de boucle. Ou est-ce que c'est ce que vous avez déjà essayé?