#java #linux #debugging
#java #linux #отладка
Вопрос:
У меня периодически возникает проблема на сервере сборки, когда Java-процесс в сборке каким-то образом не завершается и, похоже, продолжает работать (используя 100% процессора) вечно (я видел, как он выполнялся более 2 дней в выходные, когда обычно это занимает около 10 минут). kill -9 pid
кажется, это единственный способ остановить процесс.
Я попытался вызвать kill -QUIT pid
этот процесс, но, похоже, он не выдает никакой трассировки стека в стандартный вывод (возможно, он не отвечает на сигнал?). похоже, что jstack без опции принудительного ввода -F не может подключиться к запущенной JVM, но с опцией принудительного ввода он выдает вывод, приведенный ниже.
К сожалению, даже с этой трассировкой стека я не вижу никакого очевидного пути для дальнейшего расследования.
Насколько я могу судить, он показывает два «заблокированных» потока, у которых есть run Object.подождите (их стеки, похоже, содержат только основной Java-код, ничего нашего) и третий, который является ‘IN_VM’, без вывода стека.
Какие шаги я должен предпринять, чтобы собрать больше информации о причине проблемы (или, что еще лучше, как я могу ее решить)?
$ /opt/jdk1.6.0_29/bin/jstack -l -F 5546 Подключение к процессу с идентификатором 5546, пожалуйста, подождите... Отладчик успешно подключен. Обнаружен серверный компилятор. Версия JVM 20.4-b02 Обнаружение взаимоблокировки: Взаимоблокировки не обнаружены. Определение размера объекта с использованием битов Printezis и пропуск... Поток 5555: (состояние = ЗАБЛОКИРОВАНО) Заблокированные собственные синхронизаторы: - Нет Поток 5554: (состояние = ЗАБЛОКИРОВАНО) - java.lang.Object.wait (long) @bci= 0 (интерпретируемый фрейм) - java.lang.ref.ReferenceQueue.remove(длинный) @bci = 44, строка = 118 (интерпретируемый фрейм) - java.lang.ref.ReferenceQueue.remove() @bci= 2, строка = 134 (интерпретируемый фрейм) - java.lang.ref.Финализатор$FinalizerThread.run() @bci= 3, строка = 159 (интерпретируемый фрейм) Заблокированные собственные синхронизаторы: - Нет Поток 5553: (состояние = ЗАБЛОКИРОВАНО) - java.lang.Object.wait (long) @bci= 0 (интерпретируемый фрейм) - java.lang.Object.wait() @bci = 2, строка = 485 (интерпретируемый фрейм) - java.lang.ref.Reference$ReferenceHandler.run() @bci= 46, строка = 116 (интерпретируемый фрейм) Заблокированные собственные синхронизаторы: - Нет Поток 5548: (состояние = IN_VM) Заблокированные собственные синхронизаторы: - Нет
(Java версии 1.6.0, обновление 29, работает в Scientific Linux версии 6.0)
Обновить:
Запуск strace -f -p 894
создает кажущийся бесконечным поток…
[pid 900] sched_yield() = 0
[pid 900] sched_yield() = 0
...
и затем, когда Ctrl-Cd
Process 894 detached
...
Process 900 detached
...
Process 909 detached
jmap -histo 894
не устанавливает соединение, но jmap -F -histo 894
возвращает…
Подключившись к процессу с идентификатором 894, пожалуйста, подождите... Отладчик успешно подключен. Обнаружен серверный компилятор. Версия JVM 20.4-b02 Перебор кучи. Это может занять некоторое время... Определение размера объекта с использованием битов Printezis и пропуск... Определение размера объекта с использованием битов Printezis и пропуск... Гистограмма объекта: num #экземпляры #байты Описание класса -------------------------------------------------------------------------- 1: 11356 1551744 * MethodKlass 2: 11356 1435944 * ConstMethodKlass 3: 914 973488 * ConstantPoolKlass 4: 6717 849032 char[] 5: 16987 820072 * SymbolKlass 6: 2305 686048 байт[] 7: 914 672792 * InstanceKlassKlass 8: 857 650312 * ConstantPoolCacheKlass 9: 5243 167776 java.lang.Строка 10: 1046 108784 java.lang.Класс 11: 1400 87576 короткое[] 12: 1556 84040 * Системный ObjArray 13: 1037 64584 int[] 14: 103 60152 * ObjArrayKlassKlass 15: 622 54736 java.lang.reflect.Метод 16: 1102 49760 java.lang.Объект[] 17: 937 37480 java.util.TreeMap $ Запись 18: 332 27960 java.util.HashMap$Entry[] 19: 579 27792 java.nio.HeapByteBuffer 20: 578 27744 java.nio.HeapCharBuffer 21: 1021 24504 java.lang.StringBuilder 22: 1158 24176 java.lang.Класс[] 23: 721 23072 java.util.Запись HashMap $ 24: 434 20832 java.util.Древовидная карта 25: 689 18936 java.lang.Строка[] 26: 238 17440 java.lang.reflect.Метод[] 27: 29 16800 * MethodDataKlass 28: 204 14688 java.lang.reflect.Поле 29: 330 13200 java.util.LinkedHashMap$Запись 30: 264 12672 java.util.HashMap ... 585: 16 java.util.LinkedHashSet 586: 16 sun.rmi.runtime.NewThreadAction $ 2 587: 16 java.util.Хэш-таблица $ EmptyIterator 588: 16 java.util.Коллекции $ EmptySet Итого: 79700 8894800 Обход кучи занял 1.288 секунд.
Комментарии:
1. Вы можете попробовать нажать [CTRL] [SYSREQ] на консоли, где запущен сервер. Это даст вам дамп потока, который будет настолько подробным, насколько это возможно.
Ответ №1:
Вы всегда можете сделать strace -f -p pid
, чтобы увидеть, что делает Java-процесс. Судя по всему (вы не можете получить a jstack
без -F
, а поток 5548 не показывает стека вызовов и находится IN_VM), похоже, что потоку 5548 требуется слишком много времени, чтобы что-то сделать, или, возможно, он находится в каком-то бесконечном цикле.
Комментарии:
1. При запуске
strace -f -p 894
я получаю стопку строк с надписью[pid 900] sched_yield() = 0
… Интересно…2. @MattSheppard: С этого момента я бы последовал совету bestsss и получил несколько обратных следов. Если в вашей системе это произошло
pstack
, просто сделайтеpstack <pid>
. В противном случае вам пришлось бы выполнятьgdb /path/to/java <pid>
и из gdbbt
иquit
.3. Я попробую это в следующий раз, когда это произойдет.
Ответ №2:
это также может быть вызвано нехваткой памяти. Я бы попробовал две вещи :
-
Включите автоматический сброс кучи в OutOfMemory, добавив параметры JVM
-XX: HeapDumpOnOutOfMemoryError XX:HeapDumpPath=/tmp
-
Попробуйте подключиться к вашей JVM с помощью JConsole и посмотрите, есть ли какой-либо необычный шаблон
Комментарии:
1. К сожалению, jconsole, похоже, не подключается к нему (просто истекает время ожидания, если я пытаюсь удаленно, и если я запускаю его с сервера, соответствующий PID отображается серым цветом в списке). Я посмотрю, что я могу сделать, чтобы получить эти параметры для правильного вызова JVM.
Ответ №3:
Я бы заподозрил проблему с памятью. Возможно, вы захотите понаблюдать за процессом с помощью jstat и сделать дамп кучи с помощью jmap примерно в то время, когда вам нужно завершить процесс. Посмотрите, указывает ли jstat на непрерывный сбор данных. Кроме того, вы можете захотеть проверить работоспособность системы в целом (открытые файловые дескрипторы, сеть и т.д.). Память была бы самой простой, поэтому я бы настоятельно рекомендовал начать с нее.
Комментарии:
1. К сожалению, jstat выдает мне сообщение
Could not synchronize with target
. jmap, похоже, способен выдавать некоторую информацию (я обновлю вопрос выше).2. Кажется, я могу получить дублирование кучи с помощью
jmap -F -dump:format=b,file=heap.bin 894
в дополнение к гистограмме выше, но я пока не совсем уверен, что с этим делать.3. вы можете использовать eclipse MAT для просмотра дампа кучи. ищите подозреваемых в утечке. однако странно, что вы не можете использовать jstat. какую команду вы используете?
Ответ №4:
Сделайте снимок, пока процесс выполняется нормально, через jstack -F (-F должен присутствовать, он создает другой снимок, чем просто jstack). Номера потоков не Thread.id но system one. 5548, похоже, был создан до финализатора и повторного подсчета (они не являются источником проблемы), поэтому это должен быть либо поток GC, либо какой-либо компилятор.
100%, вероятно, означает какую-то ошибку в мониторе. Мониторы Java (hotspot) используют очень простой механизм блокировки вращения для обеспечения владения.
И, конечно, прикрепите debugger — GDB, чтобы проверить, где именно застрял процесс.
Ответ №5:
Поток 5554 может указывать на то, что у вас много объектов с методами finalize и / или какая-то проблема с методом finalize. Возможно, стоит взглянуть на это.
Я не был знаком с jstack, но похоже, что он выводит меньше информации, чем поток, с которым я больше знаком. Может быть полезно попытаться получить дамп потока: kill -QUIT java_pid
. Обратите внимание, что дамп отправляется в стандартный вывод, который может быть на консоль или в файл журнала в зависимости от вашей настройки.
Если сложно определить, куда направляется стандартный вывод, и предположить, что он направляется в файл, вы могли бы использовать find
время последнего изменения для идентификации файлов-кандидатов. Это предлагается в комментарии к этому сообщению в блоге:
вы можете запустить команду find[2] в своем корневом каталоге и выяснить, что изменилось за последние x секунд. Обычно я использовал find, чтобы получить доступ ко всем журналам, которые изменились за последние 10 минут, например: find /var / tomcat -mmin -3 -print (распечатывает все файлы, измененные в / var / tomcat за последние 3 минуты).
Обратите внимание, что если вы запускаете свою JVM с -Xrs
, это означает, что SIGQUIT
обработчик сигналов не будет установлен, и вы не сможете использовать это средство для запроса дампа потока.
Комментарии:
1. 5554 является завершителем и должен быть припаркован, если нет чего-то, что нужно доработать. Это не должно препятствовать завершению процесса. Завершение работы — это очень хорошая идея, поскольку это может помочь вам выяснить, что происходит с 5555, который выглядит как более вероятный виновник.
2. Я попробую
kill -QUIT
в следующий раз, когда возникнет проблема, и посмотрю, предоставит ли это дополнительную информацию, спасибо. Я думаю, что могут быть какие-то пользовательские средства завершения, но все, что они должны делать, это закрывать открытые файлы. Я полагаю, что это быстро и довольно безопасно, но, возможно, нет…3. Я думаю, что @philwb будет прав. Вероятно, с финализаторами все в порядке — поток финализатора ожидает чего-то, что нужно сделать, а не заблокирован во время работы.
4. к сожалению, kill -QUIT, похоже, не оказал никакого эффекта (т. Е. Нет вывода в файл, куда отправляется стандартный вывод процессов). Возможно, он не отвечает на сигналы (похоже, kill -9 — единственное, что его остановит).
5. Вы являетесь владельцем процесса?
Ответ №6:
Я сталкиваюсь с аналогичной проблемой, моя JBOSS jvm получает бесконечный цикл, в конечном итоге он выходит из памяти, я не могу завершить процесс, но Kill -9. Я подозреваю, что в большинстве случаев проблема с памятью.
Ответ №7:
Вот некоторые инструменты, которые вы можете использовать для локализации части процесса, потребляющей процессор:
perf
/oprofile
, особенноopannotate
— отлично подходит для просмотра того, что за чертов код использует циклыstrace
,gstack
/gdb
(как упоминалось другими)systemtap
чрезвычайно мощный, но ограниченный примерно теми же способами, что и инструменты наptrace
основе (если ваша проблема не связана с системным вызовом, это намного менее эффективно).