2009-05-31 12 views
47

Digamos que su programa Java está tomando 100% de CPU. Tiene 50 hilos. Debes encontrar qué hilo es culpable. No he encontrado una herramienta que pueda ayudar. Actualmente uso la siguiente rutina que consume mucho tiempo:¿Qué hilo Java está acaparando la CPU?

  1. Ejecute jstack <pid>, donde pid es la identificación del proceso de un proceso Java. La manera más fácil de encontrarlo es ejecutar otra utilidad incluida en el JDK - jps. Es mejor redirigir la salida de jstack a un archivo.
  2. Buscar subprocesos "ejecutables". Omita los que esperan en un socket (por alguna razón todavía están marcados como ejecutables).
  3. Repita los pasos 1 y 2 un par de veces y vea si puede ubicar un patrón.

Como alternativa, puede conectar un proceso Java en Eclipse e intentar suspender los hilos uno por uno, hasta que llegue al que acapara la CPU. En una máquina de una CPU, es posible que deba primero reducir la prioridad del proceso de Java para poder moverse. Incluso entonces, Eclipse a menudo no puede conectarse a un proceso en ejecución debido a un tiempo de espera excedido.

Hubiera esperado que la herramienta visualvm de Sun hiciera esto.

¿Alguien sabe de una mejor manera?

+0

http://stackoverflow.com/questions/755899/monitor-cpu-usage-per-thread-in-java/28408974#28408974 –

Respuesta

15

Intente mirar el Hot Thread Detector plugin for visual VM - utiliza la API de ThreadMXBean para tomar múltiples muestras de consumo de CPU para encontrar los hilos más activos. Se basa en a command-line equivalent from Bruce Chapman que también puede ser útil.

+1

Probé el detector Hot Thread de la línea de comandos de Bruce Chapman y me funcionó (necesitaba una clase de tools.jar así que tuve que copiar tools.jar a jre/lib/ext en mi Ubuntu Jaunty (9.04) con la actualización 13 de Sun Java 6). Prefiero una herramienta de línea de comando en este caso, ya que el sistema suele ser tan lento que las herramientas de GUI son un verdadero dolor. –

+5

5 años después, el enlace al archivo de Bruce Chapman ya no funciona. Todavía puede leer su publicación, pero no descargar ninguno de sus archivos. –

0

Esto es una especie de hacky, pero parece que podría iniciar la aplicación en un depurador, y luego suspender todos los los hilos, y pasar por el código y averiguar cuál no está bloqueando en un bloqueo o una llamada de E/S en algún tipo de bucle. ¿O es esto como lo que ya has intentado?

+0

sí, esto es lo que estaba haciendo más o menos. Pero esto es bastante tedioso, especialmente si hay muchos subprocesos y cuando está en una máquina de una CPU, que es el caso de una pequeña instancia de EC2 y cuando se ejecuta en VirtualBox. –

9

Simplemente ejecute JVisualVM, conéctese a su aplicación y utilice la vista de subprocesos. El que permanece continuamente activo es su culpable más probable.

+1

Incluso con JConsole (incluido con Java 1.5 y superior) puede hacer lo mismo. –

+1

JVisualVM también se incluye con J6. –

+0

Java 6 actualización 10 y posterior. –

2

Si se está ejecutando en Windows, intente Process Explorer. Abra el cuadro de diálogo de propiedades para su proceso, luego seleccione la pestaña Subprocesos.

+0

Acabamos de utilizar este enfoque para localizar un ciclo de subprocesos y funcionó bien. La vista de depuración de Eclipse es ideal para iniciar y detener subprocesos. Puede pausar todos ellos y luego reactivarlos uno por uno para ver cuál está girando. –

+0

Alternativamente, puede usar este método para encontrar el hilo problemático, registrar el ID del hilo, usar VM visual de java para realizar un volcado de hilo y encontrar el nombre del hilo problemático mediante la comparación de los identificadores de hilo. (tenga en cuenta que el ID de la cadena java visualVM está etiquetado como nid, y está en hexadecimal) – Reid

1

Eliminar un hilo. Espere 10 segundos. Toma otro volcado de hilo. Repite una vez más. Inspeccione los volcados de hilo y vea qué hilos están atascados en el mismo lugar, o procesando la misma solicitud. Esta es una forma manual de hacerlo, pero a menudo útil.

0

Una opción que podría considerar es consultar los hilos para obtener la respuesta desde la aplicación. A través del ThreadMXBean puede consultar el uso de CPU de subprocesos desde su aplicación Java y los rastros de pila de consulta de los subprocesos ofensivos.

La opción ThreadMXBean le permite construir este tipo de monitoreo en su aplicación en vivo. Tiene un impacto insignificante y tiene la clara ventaja de que puede hacer que haga exactamente lo que quiere.

0

Si sospecha que VisualVM es una buena herramienta, pruébelo (porque lo hace). Conocer los hilos (s) solo le ayuda en la dirección general de por qué consume tanta CPU.

Sin embargo, si es tan obvio, iría directamente a usar un generador de perfiles para averiguar por qué está consumiendo tanta CPU.

1

¿Está ejecutando Java 6 en una computadora con varios núcleos?

Lo más probable es que estés sufriendo de un problema que acabo de describir en un artículo sobre la falta de hilo.

Ver Synchronized vs. Lock vs. fair Lock.

6

Eche un vistazo al complemento Top Threads para JConsole.

+0

gracias, la próxima vez que entre al 100% de la CPU, compararé la herramienta de línea de comandos de Bruce y este complemento –

16

jvmtop usted puede mostrar los hilos de consumo principales:

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% 
+1

¿Alguien sabe cómo este TID puede correlacionarse con un volcado de hilo? No coinciden con los identificadores "tid" o "nid" que estoy viendo en mis volcados de hebras (después de convertirlos a hexadecimal)? – Tom

+0

Esto fue perfecto para mi uso –

57

Identificar qué Java hilo está consumiendo más CPU en el servidor de producción.

La mayoría de los sistemas productivos (si no todos) que están haciendo algo importante usarán más de 1 hilo Java. Y cuando algo se vuelve loco y el uso de su CPU es del 100%, es difícil identificar qué hilo (s) está causando esto. O eso pensé. Hasta que alguien más inteligente que yo me mostró cómo se puede hacer. Y aquí le mostraré cómo hacerlo y usted también puede sorprender a su familia y amigos con sus habilidades geek.

una aplicación de prueba

Para probar esto, se necesita una aplicación de prueba. Así que te daré uno. Se compone de 3 clases:

  • Una clase HeavyThread que hace algo intensivo de la CPU (cálculo de hashes MD5)
  • Una clase LightThread que hace algo (conteo y dormir) no tan intensivo de la CPU.
  • A StartThreads clase para iniciar 1 cpu intensivo y varios hilos ligeros.

Aquí es el código para estas clases:

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(); 
     } 
} 

Suponiendo que usted nunca ha visto este código, y todo lo que tiene un PID de un proceso de Java fuera de control que ejecuta estas clases y se consume el 100% UPC.

Primero comencemos la clase StartThreads.

$ ls 
HeavyThread.java LightThread.java StartThreads.java 
$ javac * 
$ java StartThreads & 

En esta etapa, un proceso de Java se está ejecutando debe tomar hasta 100 cpu. En mi parte superior veo: screenshot of top output

En la parte superior, presione Shift-H que enciende los hilos. La página del manual para la parte superior dice:

-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. 

Y ahora en mi top con pantalla Hilos encendido veo: top screenshot with threads displayed

Y tengo un proceso java con PID 28294.Permite obtener el volcado de pila de este proceso utilizando 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 

Desde mi top veo que el PID del hilo superior es 28938. Y 28938 en hex es 0x710A. Observe que en el volcado de pila, cada subproceso tiene un nid que se muestra en hexadecimal. Y da la casualidad de que 0x710A es el ID del hilo:

"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) 

y para que pueda confirmar que el hilo que se está ejecutando la clase HeavyThread está consumiendo más CPU.

En situaciones de mundo leídas, probablemente será un grupo de subprocesos que consumen una parte de la CPU y estos subprocesos juntos conducirán al proceso de Java utilizando 100% de CPU.

Resumen

  • principio Ejecutar
  • Presione Shift-H para permitir Temas Ver
  • una EIP del hilo con la más alta de la CPU
  • Convertir PID para Hex
  • Get volcado de pila de Java proceso
  • Busque el hilo con el HEX PID correspondiente.
+0

Este es un muy buen enfoque. Creo que este enfoque puede no funcionar cuando no está gestionando subprocesos, que a menudo es el caso cuando se utilizan marcos como Play. –

+1

¡Excelente! Muy buena respuesta. –

+3

detalle importante: use la representación hexadecimal en minúsculas – okigan

Cuestiones relacionadas