Какой поток Java забивает процессор?
Допустим, ваша программа Java принимает 100% CPU. Он имеет 50 нитей. Вам нужно найти, какая нить виновата. Я не нашел инструмент, который может помочь. В настоящее время я использую следующую очень трудоемкую процедуру:
- выполнить
jstack <pid>, где pid-идентификатор процесса процесса Java. Самый простой способ найти его-запустить другую утилиту, включенную в JDK -jps. Лучше перенаправить вывод jstack в файл. - Поиск" запускаемых " потоков. Пропустите их что ждать на сокете (по какой-то причине они все еще помечены runnable).
- повторите шаги 1 и 2 пару раз и посмотреть, если вы можете найти шаблон.
кроме того, вы можете подключиться к процессу Java в Eclipse и попытаться приостановить потоки один за другим, пока не попадете в тот, который захватывает процессор. На машине с одним процессором вам может потребоваться сначала уменьшить приоритет процесса Java, чтобы иметь возможность перемещаться. Даже тогда Eclipse часто не может подключиться к запуску процесс из-за таймаута.
Я ожидал бы Солнца visualvm инструмент для этого.
кто-нибудь знает способ лучше?
12 ответов:
попробуйте посмотреть на Hot Thread Detector плагин для visual VM -- он использует THREADMXBEAN API, чтобы взять несколько образцов потребления ЦП, чтобы найти наиболее активные потоки. Он основан на эквивалент командной строки от Брюса Чепмена что может быть полезным.
определение того, какой поток Java потребляет больше всего ЦП на рабочем сервере.
большинство (если не все) продуктивных систем, выполняющих что-либо важное, будут использовать более 1 потока java. И когда что-то сходит с ума и ресурсов процессора на 100%, трудно определить, какой поток(ы) является/являются причиной этого. Или мне так показалось. Пока кто-то умнее меня показал мне, как это можно сделать. И здесь я покажу вам, как это сделать, и Вы тоже можете удивить свою семью и друзей своим выродком умения.
Тестовое Приложение
для того, чтобы проверить это, нам нужно тестовое приложение. Так что я дам вам один. Она состоит из 3 классов:
- A
HeavyThreadкласс, который делает что-то интенсивное ЦП (вычисление MD5 хэшей)- A
LightThreadкласс, который делает что-то не очень интенсивное (подсчет и сон).- A
StartThreadsкласс для запуска 1 cpu интенсивный и несколько легких нити.вот код для этих классов:
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(); } }предполагая, что вы никогда не видели этот код, и все у вас есть PID беглого процесса Java, который работает с этими классами и потребляет 100% CPU.
сначала давайте начнем
StartThreadsкласса.$ ls HeavyThread.java LightThread.java StartThreads.java $ javac * $ java StartThreads &на данном этапе процесс Java работает должен занимать до 100 процессора. В моем топе я вижу:
в верхней части нажмите Shift-H, который включает резьбу. Этот man page for top говорит:
-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.и теперь в моем топе с включенным дисплеем потоков я вижу:
и у меня есть
javaпроцесс с PID28294. Позволяет получить дамп стека этого процесса с помощью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.access0(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сверху я вижу, что PID верхней нити
28938. И28938в hex-это0x710A. Обратите внимание, что в дампе стека, каждый поток имеетnidкоторый отображается в шестнадцатеричном формате. И так уж получилось, что0x710Ais идентификатор потока:"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.access0(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)и так вы можете подтвердить, что поток, который работает
HeavyThreadкласс потребляет большую часть процессора.в ситуациях read world это, вероятно, будет куча потоков, которые потребляют некоторую часть процессора, и эти потоки вместе взятые приведут к процессу Java с использованием 100% CPU.
резюме
- Run top
- нажмите Shift-H, чтобы включить просмотр потоков
- получить идентификатор поток с самым высоким CPU
- конвертировать PID в HEX
- получить дамп стека процесса java
- ищите нить с соответствующим шестигранным PID.
jvmtop может показать вам топ потреблял темы:
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%
просто запустите JVisualVM, подключитесь к своему приложению и используйте представление потока. Тот, кто остается постоянно активным, является вашим наиболее вероятным виновником.
посмотреть Top Threads плагин для JConsole.
Если вы работаете под Windows, то попробуйте Process Explorer. Откройте диалоговое окно Свойства для вашего процесса, затем выберите вкладку потоки.
взять дамп потока. Подождите 10 секунд. Возьмите другой дамп потока. Повторите еще раз. Проверьте дампы потоков и посмотрите, какие потоки застряли в одном и том же месте или обрабатывают один и тот же запрос. Это ручной способ сделать это, но часто полезно.
вы используете Java 6 на многоядерном компьютере?
скорее всего, вы страдаете от проблемы, которую я только что описал в статье о голодании потока.
посмотреть синхронизации и блокировки и Справедливой замок.
Я бы рекомендовал взглянуть на Артас инструмент с открытым исходным кодом от Alibaba.
в нем содержится куча полезных команд, которые могут помочь вам отладить ваш код:
- Панель управления: обзор вашего процесса Java
- SC: класс поиска, загруженный вашим JVM
- Jad: Декомпилировать Класс В Исходный Код
- посмотреть: Просмотр Ввода Вызова Метода и результаты
- Trace: найдите узкое место вашего вызова метода
- монитор: Просмотр Статистики Вызова Метода
- стек: просмотр стека вызовов метода
- Tt: временной туннель вызова метода
Это своего рода хакерский способ, но похоже, что вы можете запустить приложение в отладчике, а затем приостановить все потоки, и пройти через код и выяснить, какой из них не блокируется на блокировку или вызов ввода-вывода в какой-то цикл. Или это похоже на то, что вы уже пробовали?
вариант, который вы могли бы рассмотреть, запрашивает ваши потоки для ответа из приложения. Через ThreadMXBean вы можете запросить использование ЦП потоков из вашего приложения Java и трассировки стека запросов оскорбляющих потоков.
опция ThreadMXBean позволяет вам встроить этот вид мониторинга в ваше живое приложение. Он имеет незначительное влияние и имеет явное преимущество, что вы можете заставить его делать то, что вы хотите.
Если вы подозреваете, что VisualVM является хорошим инструментом, попробуйте его (потому что он делает это) узнать потоки(ы) только помогает вам в общем направлении, почему он потребляет так много процессора.
однако, если это так очевидно, я бы пошел прямо к использованию профилировщика, чтобы узнать, почему вы потребляете так много процессора.



Comments