Как отлаживать Segfaults, возникающие в JVM при запуске моего кода?
Мое приложение Java начало регулярно сбой с помощью SIGSEGV и дампа данных стека и загрузки информации в текстовый файл.
У меня есть отлаженные программы C в gdb, и я отлаживал Java-код из своей IDE. Я не уверен, как подойти к C-подобным сбоям в запущенной программе Java.
Я предполагаю, что я не рассматриваю ошибку JVM здесь. Другие Java-программы работают нормально, а JVM от Sun, вероятно, более стабильна, чем мой код. Тем не менее, я понятия не имею, как я могу даже вызвать segfaults с Java-кодом. Там определенно достаточно памяти, и, когда я последний раз проверялся в профилировщике, использование кучи составляло около 50% при случайных всплесках около 80%. Есть ли параметры запуска, которые я мог бы исследовать? Что такое хороший контрольный список при приближении к ошибке?
Хотя я до сих пор не способен достоверно воспроизвести событие, он, похоже, не происходит абсолютно случайно, поэтому тестирование не совсем невозможно.
ETA: некоторые детали gory
(Я ищу общий подход, так как фактическая проблема может быть очень конкретной. Тем не менее, есть некоторая информация, которую я уже собрал и которая может иметь какое-то значение.)
Некоторое время назад у меня возникла аналогичная проблема после обновления моего CI-сервера (см. здесь для получения дополнительной информации), но это исправление (установка -XX:MaxPermSize
) на этот раз не помогло.
Дальнейшее исследование показало, что в файлах журнала сбоев нить, отмеченная как "текущий поток", никогда не является моей, но либо называется "VMThread", либо называется "GCTaskThread" - это последняя, она дополнительно отмечена с комментарием "(exited)", если он первый, GCTaskThread отсутствует в списке. Это заставляет меня предположить, что проблема может быть в конце операции GC.
Ответы
Ответ 1
Я предполагаю, что я не рассматриваю ошибку JVM здесь. Другие программы Java работать просто отлично, и JVM от Sun, вероятно, более стабилен, чем мой код.
Я не думаю, что вы должны сделать это предположение. Не используя JNI, вы не сможете писать код Java, который вызывает SIGSEGV (хотя мы знаем, что это происходит). Моя точка зрения, когда это происходит, это либо ошибка в JVM (не неслыханная), либо ошибка в некотором коде JNI. Если у вас нет JNI в вашем собственном коде, это не значит, что вы не используете какую-либо библиотеку, поэтому ищите это. Когда я видел эту проблему раньше, это было в библиотеке манипуляции изображениями. Если виновник не находится в вашем собственном коде JNI, вы, вероятно, не сможете "исправить" ошибку, но вы все равно сможете обойти ее.
Сначала вы должны получить альтернативную JVM на той же платформе и попытаться ее воспроизвести. Вы можете попробовать один из этих альтернатив.
Если вы не можете воспроизвести его, это скорее ошибка JVM. Из этого вы можете либо поручить конкретную JVM, либо искать базу данных ошибок, используя то, что вы знаете о том, как ее воспроизвести, и, возможно, предложите обходные пути. (Даже если вы можете воспроизвести его, многие реализации JVM - это всего лишь трюки в реализации Oracle Hotspot, поэтому все равно может быть ошибкой JVM.)
Если вы можете воспроизвести его с помощью альтернативной JVM, ошибка может заключаться в том, что у вас есть ошибка JNI. Посмотрите, какие библиотеки вы используете и какие собственные вызовы они могут делать. Иногда есть альтернативные "чистые Java" конфигурации или файлы jar для той же библиотеки или альтернативных библиотек, которые делают почти то же самое.
Удачи!
Ответ 2
Ниже будет почти бесполезно, если у вас нет собственного кода. Однако, здесь идет.
- Запустите java-программу в java-отладчике, с точкой останова задолго до возможного sigsegv.
- Используйте команду ps для получения processid java.
- gdb/usr/lib/jvm/sun-java6/bin/java processid
- убедитесь, что команда gdb 'handle' установлена на SIGSEGV
- продолжить отладчик java от точки останова.
- ждать взрыва.
- Используйте gdb для исследования
Если вам действительно удалось заставить JVM взять sigsegv без собственного собственного кода, вы вряд ли сможете понять, что вы увидите дальше, и самое лучшее, что вы можете сделать, это нажать тестовый пример на отчет об ошибке.
Ответ 3
Я нашел хороший список в http://www.oracle.com/technetwork/java/javase/crashes-137240.html. Когда я получаю аварии во время GC, я попробую переключиться между сборщиками мусора.
Я попытался переключиться между последовательным и параллельным GC (последний по умолчанию на 64-битном Linux-сервере), это только изменило сообщение об ошибке.
Уменьшение максимального размера кучи от 16G до 10G после того, как свежий анализ в профилировщике (который дал мне использование кучи, сглаживающий на 8G), привел к значительному снижению площади "виртуальной памяти" (16G вместо 60), но Я даже не знаю, что это значит, и в Интернете говорится, что это не имеет значения.
В настоящее время JVM работает в клиентском режиме (используя параметр запуска -client
, тем самым переопределяя значение по умолчанию -server
). Пока нет краха, но влияние производительности кажется довольно большим.
Ответ 4
Если у вас есть основной файл, вы можете попробовать запустить jstack на нем, что даст вам нечто более понятное - см. http://download.oracle.com/javase/6/docs/technotes/tools/share/jstack.html, хотя, если это ошибка в gc-потоке, возможно, это не так полезно.
Ответ 5
Попробуйте проверить, есть ли c-программа carsh, вызвавшая java crash.use valgrind, чтобы узнать недействительный, а также размер контрольного стека.