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

Настройка сборки мусора для низкой латентности

Я ищу аргументы относительно того, как наилучшим образом изменить молодое поколение (в отношении старого поколения) в среде, где низкая латентность имеет решающее значение.

Мое собственное тестирование, как правило, показывает, что латентность является самой низкой, когда молодое поколение довольно велико (например, -XX: NewRatio < 3), однако я не могу примирить это с интуицией, что чем больше молодое поколение, тем больше времени следует взять на сбор мусора.

Приложение запускается на 64-битных Linux, jdk 6.

Использование памяти составляет около 50 мегабайт долгоживущих объектов, загружаемых при запуске (= кеш данных), и оттуда создается только (много) очень коротких объектов (со средней продолжительностью жизни < 1 миллисекунды).

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

4b9b3361

Ответ 1

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

Например (скажем, у вас был 32-битный jvm)

  • 3072M куча (Xms и Xmn)
  • 128M (т.е. Xmn 2944m)
  • MaxTenuringThreshold = 1
  • SurvivorRatio = 190 (т.е. каждое оставшееся в живых пространство составляет 1/192 от YG)
  • TargetSurvivorRatio = 90 (т.е. как можно больше заполнить оставшихся в живых)

Точные параметры, которые вы будете использовать для этой установки, зависят от того, какой размер установившегося состояния вашего рабочего набора (т.е. насколько жив в момент каждой коллекции). Мысль здесь явно идет вразрез с обычными правилами калибровки кучи, но тогда у вас нет приложения, которое ведет себя таким образом. Мысль состоит в том, что приложение в основном v короткоживущий мусор и немного статических данных, поэтому установите jvm вверх, чтобы эти статические данные быстро вошли в систему, а затем YG достаточно большой, чтобы он не собирался v, что сводилось к минимуму частота пауз. Вам нужно будет несколько раз нажимать регуляторы, чтобы определить, какой размер подходит для вас, и как это балансирует против размера паузы, которую вы получаете за коллекцию. Например, вы можете найти более короткие, но более частые паузы YG, например.

Вы не говорите, сколько времени работает ваше приложение, но цель здесь состоит в том, чтобы вообще не использовать накопленные коллекции для жизни приложения. Конечно, это может быть невозможно, но это стоит того.

Однако в вашем случае это не только сборник algo, но и выделение памяти. Коллекционер NUMA (совместимый только с сборщиком пропускной способности и активированный с помощью переключателя UseNUMA) использует наблюдение, что объект часто используется исключительно потоком, который его создал, и, следовательно, соответствующим образом распределяет память. Я не уверен, на чем он основан в Linux, но он использует MPO (оптимизацию размещения памяти) на Solaris, некоторые подробности в одном из альбомов ребят GC

Поскольку вы используете 64-битный jvm, убедитесь, что вы также используете CompressedOops.

Учитывая, что скорость распределения объектов (возможно, какая-то научная библиотека?) и время жизни, вы должны уделить некоторое внимание повторному использованию объектов. Одним из примеров этого lib является javalution StackContext

Наконец, стоит отметить, что паузы GC - это не единственные паузы STW, вы можете запустить с 6u21 ранний доступ, в котором есть некоторые исправления к переключателям PrintGCApplicationStoppedTime и PrintGCApplicationConcurrentTime (которые эффективно печатают время в глобальном безопасном месте и время между этими безопасными точками). Вы можете использовать флаг tracesafepointstatistics, чтобы получить представление о том, что заставляет его нуждаться в safepoint (иначе байт-код не выполняется каким-либо потоком).

Ответ 2

Вы уже включили более релевантные настройки GC, такие как выбор параллельного алгоритма с низким уровнем пауз?

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

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

Может быть, полезно отметить, что в "больших" приложениях, чувствительных к производительности, я счел необходимым сжать молодое поколение в целом. Это связано с тем, что такие приложения должны быть профилированы уже для горячих точек распределения памяти и оптимизированы, поэтому они производят несколько короткоживущих объектов. Это, в свою очередь, означает, что молодое поколение слишком сильно забивает кучу.

Итак, я предполагаю, что сначала сделаю эту оптимизацию, а затем посмотрю, как включить NewRatio за пределы 8 и посмотреть результат, заданный -verbose: gc, чтобы узнать, как GC и Full GC time торгуют и где это оптимально.

Ответ 3

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

Так как существует потребность в Java реального времени, Sun предоставляет спецификацию Java Real-Time System и имеет коммерческую реализацию. Вы можете найти более подробную информацию здесь.