Подтвердить что ты не робот

Очень странно OutOfMemoryError

Как всегда, длинное описание проблемы.

В настоящее время мы испытываем стресс-тестирование нашего продукта - и теперь мы сталкиваемся с какой-то странной проблемой. Через 1-2 часа, куча пространство начинает расти, приложение умирает когда-то позже.

Профилирование приложения показывает очень большое количество объектов Finalizer, заполняя кучу. Ну, мы подумали, что "может быть странным финализатором нить, чтобы замедлить" проблему и рассмотрели для уменьшения количества объектов, которые должны быть доработаны (в этом случае ручные дескрипторы JNA). Хорошая идея в любом случае и сокращение тысяч новых объектов...

Следующие тесты показали один и тот же шаблон, только через час и не столь крутой. На этот раз финализаторы возникли из потоков FileInput и FileOutput, которые сильно используются в тестовом стенде. Все ресурсы закрыты, но финализаторы больше не убираются.

Я понятия не имею, почему через 1 или 2 часа (без исключений) FinalizerThread внезапно перестает работать. Если мы вынуждаем System.runFinalization() вручную в некоторых наших потоках, профайлер показывает, что финализаторы очищены. Возобновление теста немедленно вызывает новое распределение кучи для финализаторов.

FinalizerThread все еще существует, спрашивая jConsole, что он ЖДЕТ.

ИЗМЕНИТЬ

Во-первых, осмотр кучи с помощью HeapAnalyzer не показал ничего нового/странного. У HeapAnalyzer есть некоторые интересные функции, но сначала у меня были трудности. Im использует jProfiler, который поставляется вместе с хорошими инструментами проверки кучи и останется с ним.

Возможно, мне не хватает некоторых функций убийцы в HeapAnalyzer?

Во-вторых, сегодня мы настраиваем тесты с помощью отладочного соединения вместо профилировщика - система стабильна в течение почти 5 часов. Кажется, это очень странная комбинация слишком много финализаторов (которые были сокращены в первом обзоре), профилировщика и стратегий виртуальной машины. Поскольку в настоящий момент все отлично работает, нет реальных идей...

Спасибо за вход до сих пор - возможно, вы остаетесь настроены и заинтересованы (теперь у вас может быть больше оснований полагать, что мы не говорим о простой ошибке программирования).

4b9b3361

Ответ 1

Я хочу закрыть этот вопрос с кратким изложением текущего состояния.

Последний тест теперь проходит более 60 часов без каких-либо проблем. Это приводит нас к следующим резюме/выводам:

  • У нас есть сервер с высокой пропускной способностью, использующий множество объектов, которые, в конце концов, реализуют "финализацию". Этими объектами являются в основном дескрипторы памяти и файловые потоки JNA. Построение финализаторов быстрее, чем GC и финализатор, могут очищаться, этот процесс терпит неудачу через ~ 3 часа. Это хорошо известное явление (- > google).
  • Мы сделали некоторые оптимизации, поэтому сервер избавился от почти всех JNA Finalizer. Эта версия была протестирована с прикрепленным jProfiler.
  • Сервер заработал несколько часов позже нашей первоначальной попытки...
  • Профилировщик показал огромное количество финализаторов, на этот раз вызванное главным образом только файловыми потоками. Эта очередь не была очищена, даже после паузы на сервере в течение некоторого времени.
  • Только после ручного запуска "System.runFinalization()" очередь была опустошена. Возобновление перезагрузки сервера...
  • Это все еще необъяснимо. Теперь мы предполагаем, что это некоторое взаимодействие профилировщика с GC/финализацией.
  • Чтобы отладить то, что могло быть причиной неактивного потока финализатора, мы отключили профайлер и на этот раз подключили отладчик.
  • Система работала без заметного дефекта... FinalizerThread и GC все "зеленые".
  • Мы возобновили тест (теперь впервые снова без каких-либо агентов, кроме jConsole), и теперь он работает и прекращается уже более 60 часов. Таким образом, по-видимому, исходный рефакторинг JNA решил проблему, только сеанс профилирования добавил некоторый индетерминизм (привет от Гейзенберга).

Другие стратегии управления финализаторами, например, обсуждаются в http://cleversoft.wordpress.com/2011/05/14/out-of-memory-exception-from-finalizer-object-overflow/ (помимо не слишком умных "не использовать финализаторы"..).

Спасибо за все ваши данные.

Ответ 2

Трудно дать конкретный ответ на вашу дилемму, но взять кучу кучи и запустить его через IBM HeapAnalyzer. Найдите "анализатор кучи в: http://www.ibm.com/developerworks (прямая связь продолжает меняться). Кажется крайне маловероятным, что поток финализатора" внезапно перестает работать", если вы не переопределяя завершение.

Ответ 3

Финализатор может быть заблокирован, но я не знаю, как он мог просто умереть.

Если у вас много методов FileInputStream и FileOutputStream finalize(), это означает, что вы не закрываете свои файлы правильно. Убедитесь, что этот поток всегда закрыт в блоке finally или использует Java 7 ARM. (Автоматическое управление ресурсами)

jConsole он ОЖИДАЕТ.

Чтобы ОЖИДАТЬ, он должен ждать объекта.

Ответ 4

Оба FileInputStream и FileOutputStream имеют одинаковые комментарии в своих методах finalize():

. . .
/*
 * Finalizer should not release the FileDescriptor if another
 * stream is still using it. If the user directly invokes
 * close() then the FileDescriptor is also released.
 */
     runningFinalize.set(Boolean.TRUE); 
. . . 

что означает, что ваш Finalizer может ждать выхода потока. Это означает, что, как упоминал выше Joop Eggen, ваше приложение может делать что-то плохое при закрытии одного из потоков.

Ответ 5

Мое предположение: это закрытие в ваших собственных потоковых (оболочных) классах. Поскольку классы потоков часто являются обертками и делегируются другим, я могу представить, что такой вложенный new A(new B(new C())) может привести к некорректной логике при закрытии. Вы должны искать двойное закрытие, делегировать закрытие. И, может быть, еще некоторые забыли закрыть (закрыть не тот объект?).

Ответ 6

С медленной растущей кучей сборщик мусора Java может исчерпать память, когда он пытается запоздало собрать мусор в ситуации с низкой памятью. Попробуйте включить параллельную метку и отладить сбор мусора с помощью - XX: + UseConcMarkSweepGC и посмотреть, не исчезла ли ваша проблема.