Как определить, что ждет мой метод?
У меня есть метод в Java, который вызывает несколько других методов. Этот метод вызывается из нескольких потоков в фиксированном пуле потоков. Количество работников совпадает с количеством доступных процессоров (ядер).
public void query() {
method1();
method2();
}
Когда я просматриваю выполнение программы с помощью VisualVM, времена method1()
и method2()
очень короткие, но query()
время очень длительное. Но метод не имеет другого кода, кроме двух вызовов. Там может быть синхронизация внутри method1()
и method2()
, но ничего не видно в коде, которым я управляю.
Когда я уменьшаю количество работников в пуле до 1, это время самопомощи почти исчезло. Как однопоточные, так и многопоточные времена выполнения всей программы практически одинаковы. Я думаю, это означает, что мой метод query()
ждет чего-то.
Нет тупиков, исполнение отлично. Два метода method1()
и method2()
вызывают много других вещей, включая классы библиотек в обфускационных баночках, поэтому мне нелегко его отлаживать. Однако метод query()
вызывается непосредственно из рабочих потоков, используя java.util.concurrent.ExecutorService
.
Ответы
Ответ 1
Я нашел проблему в прокси-классе, который обертывал другой класс в пользовательский механизм блокировки.
Я продолжил создание серии Thread Dumps. Поскольку я использовал JVisualVM для профилирования, во время процесса я создал несколько нитей Dump. Ctrl+Break
тоже работал так же, как kill -3 <pid>
, упомянутый Synesso в его ответе.
Я использовал Thread Dump Analyzer, упомянутый в комментариях для их анализа. Я не знал, что искать первым, но благодаря связыванию объектов и мониторов в TDA я нашел что-то вроде этого:
"pool-9-thread-32" #304 prio=5 os_prio=0 tid=0x000000002a706800 nid=0x348c waiting for monitor entry [0x000000003f06e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.MyClass.method1(MyClass.java:400)
- waiting to lock <0x0000000680837b90> (a com.example.DifferentClass)
at com.example.MyClass.query(MyClass.java:500)
... omitted ...
at java.util.concurrent.FutureTask.run(FutureTask.java:270)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:618)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x000000075bc59aa8> (a java.util.concurrent.ThreadPoolExecutor$Worker)
DifferentClass
расширяет абстрактный MyClass
, и есть вызов от method1()
до DifferentClass
, где объект DTO передается методу, который выполняет большую обработку, протоколирование и, наконец, сохранение в базе данных. Прокси-класс использовался во время создания одного из классов обработки базы данных.
Ответ 2
Выполните команду kill на уровне 3 против текущего процесса. Все потоки будут удалять трассировку стека по стандарту, и приложение будет продолжать работать.
kill -3 <pid>
Обратите внимание, что вы не увидите ничего на консоли, где вы выдали команду kill. Сам приложение Java будет иметь выход. Возможно, вам потребуется проверить журналы, в зависимости от того, где приложение перенаправляет свой вывод.
Ответ 3
Ваш лучший вариант - найти способ получить трассировку стека запущенной программы. Здесь - один из возможных способов.
Ответ 4
Я предлагаю запустить программу, используя режим отладки в вашей среде IDE, и поставить точки останова рядом с тем, что может показаться проблемой. Затем сделайте шаг (например, F7 в Netbeans) в точке, где программа делает задержку. Вы можете полностью вмешаться в запутанный код, хотя вы, возможно, не сможете устранить проблему там. Однако вы узнаете, где находится задержка.