Как проанализировать место создания потока на основе jstack?

Вопрос или проблема

Я столкнулся с проблемой: у сервиса много потоков, около 3000. Я проверил информацию о потоках в jstack, но не смог увидеть, где поток был создан в сервисе.

Конкретная информация о стеке потока следующая

"pool-2480-thread-3" #3699990 prio=5 os_prio=0 tid=0x00007f1a602e9000 nid=0x38764c в ожидании условия [0x00007f18bff56000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    - парковка в ожидании <0x0000000789d5f808> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

"pool-2480-thread-2" #3699989 prio=5 os_prio=0 tid=0x00007f1a602e7800 nid=0x38764b в ожидании условия [0x00007f18b9b0f000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    - парковка в ожидании <0x0000000789d5f808> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

"pool-2480-thread-1" #3699988 prio=5 os_prio=0 tid=0x00007f1a602e6000 nid=0x38764a в ожидании условия [0x00007f18bb441000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    - парковка в ожидании <0x0000000789d5f808> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

"pool-2479-thread-3" #3697117 prio=5 os_prio=0 tid=0x00007f1a64871800 nid=0x386b13 в ожидании условия [0x00007f18ba9ac000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    -parking в ожидании <0x0000000789d5bdd0> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

"pool-2479-thread-2" #3697116 prio=5 os_prio=0 tid=0x00007f1a64870000 nid=0x386b12 в ожидании условия [0x00007f18bebaf000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    - парковка в ожидании <0x0000000789d5bdd0> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

"pool-2479-thread-1" #3697115 prio=5 os_prio=0 tid=0x00007f1a644d1800 nid=0x386b11 в ожидании условия [0x00007f18b9c92000]
   java.lang.Thread.State: WAITING (парковка)
    в sun.misc.Unsafe.park(родной метод)
    - парковка в ожидании <0x0000000789d5bdd0> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    в java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    в java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    в java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    в java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    в java.lang.Thread.run(Thread.java:748)

Конкретная информация о одном потоке=>

Объект / Стек фреймов java.lang.Thread @ 0x7a044ac40

Имя pool-2492-thread-3

Мелкая куча 120

Удерживаемая куча 1,888

Макс. удерживаемая куча локалов

Загрузчик классов контекста org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader @ 0x71d665028

Является демоном ложь

Приоритет 5

Состояние [жив, припаркован, ожидающий, ждущий неопределенно]

Значение состояния 0x291

Всего: 10 записей

Ответ или решение

Для анализа местоположения создания потоков на основе информации из jstack, мы можем следовать систематическому подходу, который позволит вам идентифицировать корневую причину большого количества потоков в вашем серверном приложении.

1. Понимание контекста

Первое, что следует отметить — это то, что ваша ситуация связана с использованием пула потоков. По приведенной информации, вы видите множество потоков, находящихся в состоянии ожидания (WAITING), что может указывать на противоречия в управлении задачами, распределяемыми между потоками.

2. Анализ вывода jstack

Из предоставленного вывода jstack, мы видим, что потоки, такие как pool-2480-thread-1, pool-2480-thread-2, и pool-2480-thread-3, ожидают выполнения задач из LinkedBlockingQueue. Все они были созданы через ThreadPoolExecutor, который управляет ими. Ключевые фреймы стека, на которые стоит обратить внимание:

  • take() метода LinkedBlockingQueue — показывает, что потоки ожидают, пока задачи не станут доступны для выполнения.
  • runWorker() и run() методов ThreadPoolExecutor — это указывает на то, что потоки были созданы и управляются пулом потоков.

3. Основные шаги для поиска местоположения создания потоков

Шаг 1: Идентификация класса-создателя

Чтобы определить, где именно создаются потоки, нужно проследить, где и как используется ExecutorService, который создает эти потоки. Обычно это может быть:

  • Управляющий класс (например, @Service или @Component в Spring), который инициализирует ExecutorService.
  • Инстаграм по настройкам — если другое место в коде инициализирует ThreadPoolExecutor явно.

Шаг 2: Поиск в коде

Проведите поиск по коду на предмет использования Executors.newFixedThreadPool(), Executors.newCachedThreadPool() и других методов создания пула потоков. При этом важно учитывать:

  • Определенные настройки и максимальное количество потоков.
  • Параметры ожидания и времени жизни потоков.

Шаг 3: Логи и метрики системы

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

Шаг 4: Использование инструментов диагностики

Для более глубокого анализа можно использовать инструменты, такие как Profiler или VisualVM, которые могут показать, когда и где создаются потоки, а также общее распределение нагрузки.

4. Выявление проблем

Среди возможных причин большого количества потоков могут быть:

  • Недостаточная обработка задач, требующая того, чтобы потоки оставались в состоянии ожидания.
  • Плохо оптимизированные алгоритмы, которые требуют длительного выполнения.
  • Неправильные настройки для ThreadPoolExecutor, например, слишком высокое максимальное количество потоков.

Заключение

Анализ потока на основе jstack требует внимательного изучения не только стека вызовов, но и логики создания и управления потоками в вашем приложении. Определение точек создания потоков и оптимизация параметров пула потоков — это ключ к решению проблемы с избытком потоков. Понимание структуры вашего приложения на фоне этих анализов позволяет взять под контроль производительность системы и устранить потенциальные узкие места.

Вся эта информация поможет вам более эффективно управлять потоками и выполнять службу на стабильном уровне без чрезмерной загрузки системы.

Оцените материал
Добавить комментарий

Капча загружается...