Почему этот Java-процесс не завершается?

#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> и из gdb bt и 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 основе (если ваша проблема не связана с системным вызовом, это намного менее эффективно).