- Вопрос или проблема
- Ответ или решение
- 1. Понимание контекста
- 2. Анализ вывода jstack
- 3. Основные шаги для поиска местоположения создания потоков
- Шаг 1: Идентификация класса-создателя
- Шаг 2: Поиск в коде
- Шаг 3: Логи и метрики системы
- Шаг 4: Использование инструментов диагностики
- 4. Выявление проблем
- Заключение
Вопрос или проблема
Я столкнулся с проблемой: у сервиса много потоков, около 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
требует внимательного изучения не только стека вызовов, но и логики создания и управления потоками в вашем приложении. Определение точек создания потоков и оптимизация параметров пула потоков — это ключ к решению проблемы с избытком потоков. Понимание структуры вашего приложения на фоне этих анализов позволяет взять под контроль производительность системы и устранить потенциальные узкие места.
Вся эта информация поможет вам более эффективно управлять потоками и выполнять службу на стабильном уровне без чрезмерной загрузки системы.