web-dev-qa-db-fra.com

Quel thread Java monopolise le processeur?

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:

  1. Courir 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.
  2. Recherchez les threads "exécutables". Ignorez ceux qui attendent sur un socket (pour une raison quelconque, ils sont toujours marqués comme exécutables).
  3. Répétez les étapes 1 et 2 plusieurs fois et voyez si vous pouvez localiser un motif.

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?

55
Gene Vayngrib

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.

16
Cowan

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:

  • Une classe HeavyThread qui fait quelque chose de gourmand en CPU (calcul des hachages MD5)
  • Une classe LightThread qui fait quelque chose de moins intensif en CPU (comptage et sommeil).
  • Une classe 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: screenshot of top output

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: top screenshot with threads displayed

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

  • Run top
  • Appuyez sur Shift-H pour activer la vue Threads
  • Obtenez le PID du thread avec le processeur le plus élevé
  • Convertir PID en HEX
  • Obtenir le vidage de pile de Java
  • Recherchez le fil avec le HEX PID correspondant.
73
Vassilis Blazos

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%
18
MRalwasser

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.

11
Lawrence Dol

Jetez un œil au plugin Top Threads pour JConsole.

6
Mark

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.

2
jdigital

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:

  • Tableau de bord : Présentation de votre processus Java
  • [~ # ~] sc [~ # ~] : classe de recherche chargée par votre JVM
  • Jad : décompiler la classe en code source
  • Regarder : Afficher l'entrée et les résultats de l'invocation de la méthode
  • Trace : trouvez le goulot d'étranglement de votre invocation de méthode
  • Moniteur : Afficher les statistiques d'invocation de méthode
  • Stack : Afficher la pile d'appels de la méthode
  • Tt : tunnel temporel des invocations de méthode

Exemple de tableau de bord: dashboard

1
Serge

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.

Voir Synchronized vs Lock vs fair Lock .

1
Huxi

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.

1
talonx

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.

0
Neil Coffey

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.

0
Peter Lawrey

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é?

0
Paul Fisher