Я запускаю приложение, которое создает и забудет большое количество объектов, количество длинных существующих объектов медленно растет, но это очень мало по сравнению с короткоживущими объектами. Это настольное приложение с высокими требованиями к доступности, его необходимо включить 24 часа в сутки. Большая часть работы выполняется в одном потоке, этот поток будет использовать весь процессор, который он может получить.
В прошлом мы видели следующее при большой нагрузке:
Используемое кучное пространство медленно увеличивается, поскольку сборщик мусора собирает меньше объема вновь выделенной памяти, размер используемой кучи медленно растет и в конечном итоге приближается к указанной максимальной куче. В этот момент сборщик мусора сильно ударит и начнет использовать огромное количество ресурсов, чтобы предотвратить превышение максимального размера кучи. Это замедляет приложение вниз (легко 10x как медленное), и в этот момент большую часть времени GC будет успешно очищать мусор через несколько минут или терпеть неудачу и бросать OutOfMemoryException
, оба из них на самом деле не приемлемы.
Используемое оборудование представляет собой четырехъядерный процессор с объемом памяти не менее 4 ГБ с 64-разрядным Linux, и все это мы можем использовать в случае необходимости. В настоящее время приложение сильно использует одно ядро, которое использует большую часть своего времени для работы с одним ядром/потоком. Другие ядра в основном простаивают и могут использоваться для сбора мусора.
У меня возникает ощущение, что сборщик мусора должен собираться более агрессивно на ранней стадии, задолго до того, как у него закончится память. Наше приложение не имеет проблем с пропускной способностью, требования к минимальному времени ожидания немного важнее пропускной способности, но гораздо менее важно, чем не приближаться к максимальному размеру кучи. Это приемлемо, если один занятый поток работает только на 75% от текущей скорости, если это означает, что сборщик мусора может идти в ногу с созданием. Короче говоря, неуклонное снижение производительности лучше, чем внезапное падение, которое мы видим сейчас.
Я прочитал Java SE 6 HotSpot [tm] Настройка виртуальной машины Garbage Collection Tuning, что означает, что я хорошо понимаю варианты, однако я по-прежнему трудно выбрать правильные настройки, так как мои требования немного отличаются от того, что обсуждается в документе.
В настоящее время я использую ParallelGC с опцией -XX:GCTimeRatio=4
. Это работает немного лучше, чем значение по умолчанию для отношения времени, но у меня есть ощущение, что GC имеет возможность запускать больше, чем это делает.
Для мониторинга я использую jconsole и jvisualvm в основном.
Я хотел бы знать, какие варианты сбора мусора вы рекомендуете для вышеуказанной ситуации. Кроме того, какой GC отладочный вывод я могу посмотреть, чтобы лучше понять шейку бутылки.
EDIT: Я понимаю, что очень хороший вариант заключается в том, чтобы создать меньше мусора, это то, что мы действительно рассматриваем, однако я хотел бы знать, как мы можем справиться с этим с помощью настройки GC, поскольку это то, что мы можем сделать гораздо легче и развернуть больше быстрее, чем изменение большого количества исходного кода. Кроме того, я запускал разные профилировщики памяти, и я понимаю, что использует мусор, и я знаю, что он состоит из объектов, которые могут быть собраны.
Я использую:
java version "1.6.0_27-ea"
Java(TM) SE Runtime Environment (build 1.6.0_27-ea-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.2-b03, mixed mode)
С параметрами JVM:
-Xmx1024M and -XX:GCTimeRatio=4
Изменить в ответ на комментарии Matts: Большая часть памяти (и процессор) идет на создание объектов, которые представляют текущую ситуацию. Некоторые из них сразу же будут отброшены, так как ситуация быстро меняется, некоторые другие будут иметь средний срок службы, если обновления не появятся некоторое время.