«1. Обзор

Поток — это основная единица параллелизма в Java. В большинстве случаев пропускная способность приложения увеличивается, когда создается несколько потоков для параллельного выполнения задач.

Однако всегда есть точка насыщения. В конце концов, пропускная способность приложения зависит от ресурсов процессора и памяти. После определенного предела увеличение количества потоков может привести к переполнению памяти, переключению контекста потока и т. д.

Таким образом, хорошей отправной точкой в ​​устранении проблемы с нехваткой памяти в приложении Java является отслеживание количества потоков. В этом руководстве мы рассмотрим некоторые способы проверки количества потоков, созданных процессом Java.

2. Графические инструменты мониторинга Java

Самый простой способ увидеть количество потоков в Java — использовать графический инструмент, такой как Java VisualVM. Помимо потоков приложения, Java VisualVM также перечисляет GC или любые другие потоки, используемые приложением, такие как потоки JMX.

Кроме того, он также показывает статистику, такую ​​как состояния потоков, а также их продолжительность:

Java VisualVM

Мониторинг количества потоков является основной функцией Java VisualVM. Вообще говоря, графические инструменты более продвинуты и позволяют отслеживать приложение в реальном времени. Например, Java VisualVM позволяет нам отбирать трассировки стека ЦП и, таким образом, находить класс или метод, которые могут вызвать узкое место ЦП.

Java VisualVM распространяется вместе с установкой JDK на машинах Windows. Для приложений, развернутых в Linux, нам необходимо удаленно подключиться к приложению. Для этого требуются аргументы виртуальной машины JMX.

Поэтому такие инструменты не будут работать, если приложение уже запущено без этих параметров. В следующем разделе мы увидим, как мы можем получить количество потоков с помощью инструментов командной строки.

3. Java API

В некоторых случаях нам может понадобиться узнать количество потоков внутри самого приложения. Например, для отображения на панелях мониторинга или в журналах.

В таких случаях мы полагаемся на API-интерфейсы Java, чтобы получить количество потоков. К счастью, в классе Thread есть API activeCount():

public class FindNumberofThreads {
    public static void main(String[] args) {
        System.out.println("Number of threads " + Thread.activeCount());
    }
}

И вывод будет таким:

Number of threads 2

Примечательно, что если мы увидим количество потоков в Java VisualVM, мы увидим больше потоков. для того же приложения. Это связано с тем, что activeCount() возвращает только количество потоков в одной и той же группе ThreadGroup. Java делит все потоки на группы для облегчения управления.

В этом примере у нас есть только родительская группа ThreadGroup, т. е. main:

public static void main(String[] args) {
    System.out.println("Current Thread Group - " + Thread.currentThread().getThreadGroup().getName());
}
Current Thread Group - main

Если в Java-приложении много групп потоков, функция activeCount() не даст правильного результата. Например, он не вернет количество потоков GC.

public static void main(String[] args) {
    System.out.println("Total Number of threads " + ManagementFactory.getThreadMXBean().getThreadCount());
}

В таких сценариях мы можем использовать JMX API:

Total Number of threads 6

Этот API возвращает общее количество потоков из всех групп потоков, GC, JMX и т. д.:

В зависимости от Фактически, графические инструменты JMX, такие как Java VisualVM, используют один и тот же API для своих данных.

4. Инструменты командной строки

Ранее мы обсуждали Java VisualVM, графический инструмент для анализа активных потоков в приложении. Хотя это отличный инструмент для визуализации потоков в реальном времени, он оказывает незначительное влияние на производительность приложения. И, следовательно, это не рекомендуется для производственных сред.

Кроме того, как мы уже говорили, Java VisualVM требует удаленного подключения в Linux. И ведь в некоторых случаях требует дополнительной настройки. Например, приложение, работающее внутри Docker или Kubernetes, потребует дополнительной настройки службы и порта.

В таких случаях мы должны полагаться на инструменты командной строки в хост-среде, чтобы получить количество потоков.

К счастью, в Java есть несколько команд для создания дампа потока. Мы можем проанализировать дамп потока либо как текстовый файл, либо использовать инструмент анализатора дампа потока, чтобы проверить количество потоков вместе с их состоянием.

Alibaba Arthas — еще один отличный инструмент командной строки, не требующий удаленного подключения или какой-либо специальной настройки.

top -H -p 1

«Кроме того, мы можем получить информацию о потоках из нескольких команд Linux. Например, мы можем использовать команду top для отображения всех потоков любого Java-приложения:

top - 15:59:44 up 7 days, 19:23,  0 users,  load average: 0.52, 0.41, 0.36
Threads:  37 total,   0 running,  37 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.2 us,  2.2 sy,  0.0 ni, 93.4 id,  0.8 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem :   1989.2 total,    110.2 free,   1183.1 used,    695.8 buff/cache
MiB Swap:   1024.0 total,    993.0 free,     31.0 used.    838.8 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   1 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.07 java
  275 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:02.87 java
  276 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.37 VM Thread
  277 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Reference Handl
  278 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Finalizer
  279 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Signal Dispatch

Здесь -H — это параметр командной строки для отображения каждого потока в процессе Java. Без этого флага команда top будет отображать объединенную статистику для всех потоков в процессе. Параметр -p фильтрует выходные данные по идентификатору процесса целевого приложения:

Как показано выше, он показывает идентификатор потока, т. е. PID и использование ЦП и памяти для каждого потока. Подобно Java VisualVM, команда top выведет список всех потоков, включая GC, JMX или любой другой подпроцесс.

ps -ef | grep java

Чтобы найти идентификатор процесса, который мы использовали в качестве аргумента в приведенной выше команде, мы можем использовать команду ps:

ps -e -T | grep 1

На самом деле, мы также можем использовать команду ps для получения списка потоков:

1     1 ?        00:00:00 java
1   275 ?        00:00:02 java
1   276 ?        00:00:00 VM Thread
1   277 ?        00:00:00 Reference Handl
1   278 ?        00:00:00 Finalizer
1   279 ?        00:00:00 Signal Dispatch
1   280 ?        00:00:03 C2 CompilerThre
1   281 ?        00:00:01 C1 CompilerThre

Параметр -T сообщает команде ps список всех потоков, запущенных приложением:

Здесь первый столбец — это PID, а второй столбец показывает идентификатор потока Linux для каждого нить.

5. Заключение

В этой статье мы увидели, что существуют различные способы определения количества потоков в приложении Java. В большинстве случаев следует использовать параметры командной строки, такие как команда top или ps.