Что вы делаете, когда ваше Java-приложение потребляет 100% ресурсов ЦП? Оказывается, проблемные потоки можно легко найти с помощью встроенных утилит Unix и JDK. Никаких инструментов профилирования не требуется.
Для тестирования мы будем использовать простую программу:
Как видите, в этом фрагменте кода запускаются 2 потока.public class Main { public static void main(String[] args) { new Thread(new Idle(), "Idle").
start(); new Thread(new Busy(), "Busy").
start(); } } class Idle implements Runnable { @Override public void run() { try { TimeUnit.HOURS.sleep(1); } catch (InterruptedException e) { } } } class Busy implements Runnable { @Override public void run() { while(true) { "Foo".
matches("F.*"); } } }
В режиме ожидания не потребляются ресурсы процессора( помните, что спящие потоки потребляют память, но не процессор), в то время как Busy сильно нагружает процессор, выполняя анализ регулярных выражений и другие сложные процессы.
Как мы можем быстро найти проблемный фрагмент кода в нашей программе? Сначала мы будем использовать «top», чтобы найти идентификатор процесса (PID) Java-приложения.
Это довольно просто: top -n1 | grep -m1 java
Мы увидим первую строку вывода «top», содержащую слово «java»: 22614 tomek 20 0 1360m 734m 31m S 6 24.3 7:36.59 java
Первый столбец — PID. К сожалению, оказывается, что «top» использует Escape-коды ANSI для цветов .
К счастью, я нашел Perl-скрипт для удаления лишних символов и, наконец, извлечения PID. top -n1 | grep -m1 java | perl -pe 's/\e\[?.
*?[\@-~] ?//g' | cut -f1 -d' '
Возврат: 22614
Теперь, когда мы знаем PID процесса, мы можем использовать top -H для поиска проблемных потоков Linux. Переключатель -H отображает список всех потоков, и теперь столбец PID — это идентификатор потока: top -n1 -H | grep -m1 java
top -n1 -H | grep -m1 java | perl -pe 's/\e\[?.
*?[\@-~] ?//g' | cut -f1 -d' '
Возврат: 25938 tomek 20 0 1360m 748m 31m S 2 24.8 0:15.15 java
25938
Итого у нас есть идентификатор процесса JVM и идентификатор потока Linux. Теперь самое интересное: если вы посмотрите на выходные данные jstack (доступны в JDK), каждый поток имеет NID, который пишется после имени.
Busy' prio=10 tid=0x7f3bf800 nid=0x6552 runnable [0x7f25c000]
java.lang.Thread.State: RUNNABLE
at java.util.regex.Pattern$Node.study(Pattern.java:3010)
Параметр nid=0x6552 представляет собой шестнадцатеричное представление идентификатора потока: printf '%x' 25938
6552
Теперь объединим все в один скрипт: #!/bin/bash
PID=$(top -n1 | grep -m1 java | perl -pe 's/\e\[?.
*?[\@-~] ?//g' | cut -f1 -d' ') NID=$(printf '%x' $(top -n1 -H | grep -m1 java | perl -pe 's/\e\[?.
*?[\@-~] ?//g' | cut -f1 -d' '))
jstack $PID | grep -A500 $NID | grep -m1 '^$' -B 500
Последняя строка запускает jstack с определенным PID и выводит поток с соответствующим NID. Тот же поток будет проблематичным.
Мы делаем: .
/profile.sh
"Busy" prio=10 tid=0x7f3bf800 nid=0x6552 runnable [0x7f25c000]
java.lang.Thread.State: RUNNABLE
at java.util.regex.Pattern$Node.study(Pattern.java:3010)
at java.util.regex.Pattern$Curly.study(Pattern.java:3854)
at java.util.regex.Pattern$CharProperty.study(Pattern.java:3355)
at java.util.regex.Pattern$Start.<init>(Pattern.java:3044)
at java.util.regex.Pattern.compile(Pattern.java:1480)
at java.util.regex.Pattern.<init>(Pattern.java:1133)
at java.util.regex.Pattern.compile(Pattern.java:823)
at java.util.regex.Pattern.matches(Pattern.java:928)
at java.lang.String.matches(String.java:2090)
at com.blogspot.nurkiewicz.Busy.run(Main.java:27)
at java.lang.Thread.run(Thread.java:662)
Источник
Теги: #java #threads #java
-
Начните Интернет-Бизнес С Домашним Альянсом
19 Oct, 24 -
Amd Ryzen: Взгляд Изнутри
19 Oct, 24 -
Я Богат
19 Oct, 24 -
Задержка Доставки Почты С Мастерхоста
19 Oct, 24