Java Garbage Collector - не работает нормально через регулярные промежутки времени - PullRequest
15 голосов
/ 30 ноября 2010

У меня есть программа, которая постоянно работает.Обычно кажется, что сборщик мусора занимает около 8 МБ памяти.Однако каждые выходные он отказывается собирать мусор, если я не сделаю явный вызов.Однако, если он приближается к максимальному размеру кучи, он все равно будет собирать мусор.Однако единственная причина, по которой эта проблема была замечена, заключается в том, что она фактически вылетала из-за нехватки памяти в один из выходных, то есть она достигла максимального размера кучи и не запустила сборщик мусора.

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

Что здесь происходит?

РЕДАКТИРОВАТЬ:

Хорошо, судя по комментариям, я не предоставил достаточно информации.Программа просто получает поток пакетов UDP, которые помещаются в очередь (максимальный размер которых составляет 1000 объектов), которые затем обрабатываются для сохранения их данных в базе данных.В среднем он получает около 80 пакетов в секунду, но может достигать пика до 150. Он работает под управлением Windows Server 2008.

Дело в том, что это действие достаточно согласованно и, если вообще, во время, когда памятьиспользование начинает расти, активность должна быть ниже, а не выше.Напомним, что график, который я выложил выше, - единственный, который у меня есть, который простирается до этого уровня, поскольку я изменил только оболочку Java Visual VM, чтобы сохранить данные графика достаточно далеко, чтобы увидеть их на этой неделе, поэтому я понятия не имею, точно ли этов одно и то же время каждую неделю, потому что я не могу смотреть его в выходные дни, так как он находится в частной сети, и я не на работе в выходные дни.

Вот график следующего дня:alt text

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

Тот факт, что он успешно собирает мусор, собирает все эти объекты, когда мы говорим об этом, подразумевает, что объекты можно собирать, он просто не делает этого, пока не достигнет максимального размера кучи, или мы явно не вызовем сборщик мусора.Дамп кучи ничего не говорит нам, потому что, когда мы пытаемся его выполнить, он неожиданно запускает сборщик мусора, а затем выводит дамп кучи, что, конечно, выглядит совершенно нормально на этом этапе.

Итак, я полагаю, у меня есть два вопроса: почему он вдруг не собирает мусор так, как это делает остаток недели, и почему в одном случае сбор мусора происходит, когда он достигает максимумаРазмер кучи был неспособен собрать все эти объекты (то есть, почему бы было так много ссылок на столько объектов, что когда-то не должно быть)?

ОБНОВЛЕНИЕ:

Это утро было интересным.Как я уже упоминал в комментариях, программа работает на клиентской системе.Наш контакт в организации-клиенте сообщает, что в 1 час утра эта программа завершилась сбоем, и ему пришлось перезапустить ее вручную, когда он приступил к работе этим утром, и что снова серверное время было неправильным.Это проблема, с которой мы сталкивались в прошлом, но до сих пор эта проблема, казалось, никогда не была связана.

Просматривая журналы, которые создает наша программа, мы можем вывести следующую информацию:

  1. В 01:00 сервер каким-то образом повторно синхронизировал свое время, установив его на 00:28.
  2. В 00:45 (по новому, неправильному времени сервера) один из потоков обработки сообщений в программе выдал ошибку нехватки памяти.
  3. Однако другой поток обработки сообщений (есть два типа сообщений, которые мы получаем, они обрабатываются немного по-разному, но оба они постоянно поступают), продолжает работать, и, как обычно, использование памяти продолжает расти с нет сборки мусора (как видно из графиков, которые мы записывали, еще раз).
  4. В 00:56 журналы останавливаются примерно до 7 утра, когда наш клиент перезапустил программу. Однако график использования памяти за это время все еще неуклонно увеличивался.

К сожалению, из-за изменения времени сервера, это делает время на нашем графике использования памяти ненадежным. Тем не менее, похоже, что он попытался собрать мусор, потерпел неудачу, увеличил пространство кучи до максимально доступного размера и сразу уничтожил этот поток. Теперь, когда максимальный объем кучи увеличился, он счастлив использовать все это, не выполняя большую сборку мусора.

Итак, теперь я спрашиваю: если время сервера внезапно меняется, как это произошло, может ли это вызвать проблемы с процессом сбора мусора?

Ответы [ 3 ]

11 голосов
/ 30 ноября 2010

Однако единственная причина, по которой эта проблема была замечена, заключается в том, что она фактически вылетала из-за нехватки памяти в один из выходных, т. Е. Она достигла максимального размера кучи и не запускала сборщик мусора.

Я думаю, что ваш диагноз неверен. Если в вашей виртуальной машине Java что-то серьезно не работает, приложение выдаст OOME после , оно только что запустило полный сборщик мусора и обнаружило, что все еще недостаточно Свободная куча для продолжения *.

Я подозреваю, что здесь происходит одно или несколько из следующих действий:

  • Ваше приложение имеет медленную утечку памяти. Каждый раз, когда вы перезапускаете приложение, утечка памяти восстанавливается. Таким образом, если вы регулярно перезапускаете приложение в течение недели, это может объяснить, почему оно вылетает только в выходные дни.

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

Запуск GC вручную не решит проблему в любом случае. Что вам нужно сделать, это изучить возможность утечек памяти, а также посмотреть на размер памяти приложения, чтобы убедиться, что он достаточно велик для выполняемых задач.

Если вы сможете фиксировать статистику кучи за длительный период, утечка памяти со временем будет отображаться как тенденция к уменьшению объема памяти, доступного после полных сборок мусора. (Это высота самых длинных «зубцов» пилообразного шаблона.) Недостаток памяти, связанный с рабочей нагрузкой, вероятно, проявится в виде случайного резкого нисходящего тренда в той же мере в течение относительно короткого периода времени, за которым следует восстановление. Вы можете видеть и то и другое, тогда у вас могут случиться обе вещи.

* На самом деле, критерии принятия решения о том, когда отказаться от ООМ, немного сложнее, чем этот. Они зависят от определенных параметров настройки JVM и могут включать процент времени, потраченного на работу ГХ.

Followup

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

С вашим новым доказательством есть еще две возможности:

  • Ваше приложение может застрять в цикле, который приводит к утечке памяти из-за изменения времени часов.

  • Деформация времени часов может привести к тому, что GC сочтет, что он занимает слишком большой процент времени работы, и в результате вызовет OOME. Это поведение зависит от настроек JVM.

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

(Относительно 2-го пункта: в JVM есть механизм мониторинга GC, который измеряет процент общего времени, которое JVM тратит на работу GC, относительно выполнения полезной работы. Это разработано для предотвращения измельчения JVM до остановка, когда вашему приложению действительно не хватает памяти.

Этот механизм будет реализован путем выборки времени настенных часов в различных точках. Но если время настенных часов ограничено во времени в критической точке, легко увидеть, как JVM может подумать, что конкретный прогон GC занял намного больше времени, чем на самом деле ... и вызвать OOME.)

2 голосов
/ 13 декабря 2010

Хорошо, ребята, спасибо за вашу помощь. Однако правильный ответ, как оказалось, не имеет ничего общего с самой программой.

Похоже, что в то время, когда использование памяти начало расти, сервер синхронизировал свое время откуда-то изнутри, хотя ИТ-контакт нашего клиента не знает, где. Очевидно, откуда бы они ни исходили, не были хорошие часы, так как время было на полчаса позади. Мы отключили эту синхронизацию, и теперь, когда я проверил ее сегодня утром, проблема не возникла. Поэтому, если время в вашей системе внезапно меняется, очевидно, это вызывает проблемы для сборщика мусора. По крайней мере, это то, что это значит для меня.

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

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

2 голосов
/ 01 декабря 2010

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

Вот параметры JVM, взятые со страницы Oracle Java HotSpot VM Options . (Предполагается, что у вас есть Oracle JVM):

-XX:. HeapDumpPath = / java_pid.hprof

Путь к каталогу или имя файла для кучи свалка. Управляемость. (Введено в 1.4.2 обновление 12, 5.0 обновление 7.)

-XX: -HeapDumpOnOutOfMemoryError

Дамп кучи, когда файл java.lang.OutOfMemoryError выбрасывается. Управляемость. (Введено в 1.4.2 обновление 12, 5.0 обновление 7.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...