Сравнение производительности HotSpot и OpenJ9
Сентябрь 16, 2022
Производительность приложений во многом зависит от возможностей и правильной настройки виртуальной машины Java. Сегодня двумя самыми популярными опенсорсными реализациями JVM являются HotSpot и OpenJ9. HotSpot, созданная Sun Microsystems, развивается в рамках проекта OpenJDK. OpenJ9 была разработана IBM и входит в проект Eclipse Foundation. Вопрос в том, какую из них выбрать?
В некоторых сравнительных исследованиях (например, в бенчмарке DayTrader7) OpenJ9 демонстрирует лучшие показатели времени запуска и отклика, пропускной способности и потребления памяти. Но важно отметить, что параметры OpenJ9 были оптимизированы, а HotSpot была настроена по умолчанию. Инженеры команды Axiom JDK решили проверить, можно ли сконфигурировать HotSpot таким образом, чтобы получить схожий или лучший результат по сравнению с OpenJ9.
О результатах исследования читайте в нашей статье.
Условия тестирования
В качестве бенчмарка мы использовали приложение DayTrader7 из исходного исследования. Это не микросервисное приложение, а небольшой монолит, запущенный на веб-сервере. Использованное оборудование и ПО:
- Сервер на Linux
- Десктоп на Windows
- Дистрибутивы OpenJDK 11: сборка AdoptOpenJDK с тремя конфигурациями OpenJ9 и сборка Axiom JDK с дефолтной и конфигурированной HotSpot
В ходе исследования мы измеряли время запуска, потребление памяти, время отклика и пропускную способность. Три последних показателя регистрировались с помощью Apache Meter 5.4.1.У нас не было цели получить искусственные показатели посредством тонкой настройки HotSpot. Вместо этого мы изучали возможности стандартных настроек этой JVM.
Результаты
Время запуска
На уменьшение времени запуска приложений, использующих HotSpot, влияют несколько параметров. Первый — Application Class Data Sharing (AppCDS). Эта фича позволяет размещать классы приложения в общем архиве, тем самым ускоряя запуск. Она появилась в HotSpot 1.5 и была улучшена в последующих версиях. Например, теперь можно автоматически генерировать архивы. Архив AppCDS необходимо создавать эксплицитно.
Для включения AppCDS используйте флаг -XX:SharedArchiveFile=app-cds.jsa
. Параметр позволяет указать имя архивного файла для хранения классов. Без него классы будут храниться в установочной директории JDK, что нежелательно. После создания архива JVM при запуске приложения размещает их в памяти, где они будут доступны при следующем запуске. Подробнее об AppCDS можно почитать на сайте Oracle (на английском языке).
Помимо этого, параметр -XX:TieredStopAtLevel=1
позволяет разработчикам запускать приложение с клиентским компилятором (C1) без профайлинга, благодаря чему приложение компилируется быстрее. Такая конфигурация подходит для ситуаций, когда быстрый запуск крайне важен.
Ниже приведено сравнение времени запуска с OpenJ9 и HotSpot. Время запуска вычислялось на основании разницы между моментом запуска и выводом «Application daytrader7 started in X seconds».
Конфигурации запуска:
- OpenJ9 (1) (размер кучи 256 Мб): -Xmx256m
- OpenJ9 (2) (размер кучи 256 Мб, кеш классов): -Xmx256m -Xshareclasses:name=mvn
- OpenJ9 (3) (размер кучи 256 Мб, кеш классов, быстрая компиляция): -Xmx256m -Xshareclasses:name=mvn -Xtune:virtualized -Xscmx200m
- HotSpot (размер кучи 256 Мб): -Xmx256m
- HotSpot (размер кучи 256 Мб, AppCDS, C1): -Xmx256m -XX:SharedArchiveFile=app-cds.jsa -XX:TieredStopAtLevel=1
Время запуска (с) | ||||
---|---|---|---|---|
OpenJ9 (1) (размер кучи 256 Мб) | OpenJ9 (2) (размер кучи 256 Мб, кеш классов) | OpenJ9 (3) (размер кучи 256 Мб, кеш классов, быстрая компиляция) | HotSpot (размер кучи 256 Мб) | HotSpot (размер кучи 256 Мб, AppCDS, C1) |
6,986 с | 6,064 с | 6,159 с | 6,366 с | 4,972 с |
Axiom JDK с указанными параметрами продемонстрировала лучший результат (4,972 с), а худший результат (6,986 с) показала OpenJ9 без дополнительных параметров, кроме ограничения размера кучи.
Потребление памяти
В нашем исследовании мы использовали тест-план daytrader7.jmx от разработчиков Daytrader7. Параметры тест-плана были изменены в соответствии с классом машины: на сервере использовались четыре потока, на десктопе — шесть потоков.
Тест-план представляет собой скрипт с множеством HTTP-запросов, имитирующих работу пользователя в торговой онлайн-системе. Пользователь может входить в систему, выходить из нее, смотреть портфолио или котировку акций, покупать и продавать акции.
Перед сбором статистики был выполнен разогрев виртуальной машины. После этого выполнялось в среднем по 5–7 итераций, 120–300 с каждая, с перерывами в 30 с. Затем рассчитывалось среднее значение. Перед замером каждой конфигурации (разогревочный прогон) создавалась база данных daytrader с 15 тысячами пользователей. База данных сбрасывалась перед каждой итерацией.
При испытаниях под нагрузкой дефолтная HotSpot потребляет больше памяти, чем OpenJ9. Но параметры -XX:SharedArchiveFile=app-cds.jsa
и -XX:TieredStopAtLevel=1
, уже использованные нами для сокращения времени запуска, обеспечили снижение потребления памяти на 30%. Такой результат возможен благодаря тому, что компилятор C1 потребляет меньше памяти в кеше кода ввиду отсутствия промежуточных выражений скомпилированного кода. При этом AppCDS дополнительно снижает потребление памяти благодаря метаданным о классах, расшаренным между разными JVM.
Конфигурации запуска:
- OpenJ9 (1) (размер кучи 1 Гб): -Xmx1G
- OpenJ9 (2) (размер кучи 1 Гб, кеш классов): -Xmx1G -Xshareclasses:name=mvn
- OpenJ9 (3) (размер кучи 1 Гб, кеш классов, быстрая компиляция): -Xmx1G -Xshareclasses:name=mvn -Xtune:virtualized -Xscmx200m
- HotSpot (размер кучи 1 Гб): -Xmx1G
- HotSpot (размер кучи 1 Гб, AppCDS, C1): -Xmx1G -XX:SharedArchiveFile=app-cds.jsa -XX:TieredStopAtLevel=1
Потребление памяти (через ~126 с) | ||||
---|---|---|---|---|
OpenJ9 (1) (размер кучи 1 Гб) | OpenJ9 (2) (размер кучи 1 Гб, кеш классов) | OpenJ9 (3) (размер кучи 1 Гб, кеш классов, быстрая компиляция) | HotSpot (размер кучи 1 Гб) | HotSpot with (размер кучи 1 Гб, AppCDS, C1) |
418,6 Мб | 423,7 Мб | 389,6 Мб | 612,8 Мб | 424,7 Мб |
Тест-план включал в себя комбинацию разных запросов, выполняющих сложные операции. Красными точками отмечено время запуска приложения. Максимальный размер кучи был установлен на 256 Мб (флаг -Xmx256m
). Показатели измерялись через ~126 с после начала тестирования, поскольку у разных конфигураций HotSpot и OpenJ9 разное время запуска. Стоит отметить, что потребление памяти сразу после запуска приложения повышается, а затем стабилизируется. На графике видно, что перед стабилизацией у OpenJ9 происходят скачки в потреблении памяти. Эта память необходима для разогрева, но все равно получается, что приложение потребляет больше памяти, чем в стабильном состоянии. Поэтому если мы установим предел потребления памяти, ориентируясь на показатели после разогрева, время стабилизации может увеличиться, и пиковая производительность упадет.
Оптимизация скачков в потреблении памяти проводится в рамках комплексной стратегии. Например, Axiom Lite, легковесная версия Axiom JDK, может высвобождать память при работе без нагрузки, а в OpenJ9 недавно была добавлена функция JIT as a Service, которая позволяет виртуальной машине аккумулировать пиковое потребление памяти нескольких инстансов на одном сервисе компиляции.
Кроме того, с Axiom Lite можно создавать микроконтейнеры, занимающие в несколько раз меньше памяти, чем стандартные Java-контейнеры. Это особенно важно в облачной разработке, где важен каждый мегабайт. Контейнер на базе Axiom Lite и Alpine Linux весит всего 42,72 Мб — это самый маленький контейнер на рынке!
Но вернемся к исследованию. Нашей целью была оценка потребления памяти в стабилизированном состоянии. Через 126 секунд все конфигурации виртуальных машин достигли стабильных показателей, которые можно было измерить. На графике видно, что результаты HotSpot с дополнительными параметрами аналогичны результатам двух конфигураций OpenJ9 или превосходят их.
Время отклика и пропускная способность
Для этой серии тестов максимальный размер кучи был установлен на 1 Гб (-Xmx1G
). Таким образом, мы смогли проанализировать поведение приложения в пределах небольшой ноды в облаке.
Дефолтная конфигурация HotSpot продемонстрировала лучшую пропускную способность (на 5% выше, чем у лучшей конфигурации OpenJ9) и время отклика (на 69% ниже, чем у лучшей конфигурации OpenJ9), но потребляла больше памяти, чем OpenJ9. Флаги -XX:SharedArchiveFile=app-cds.jsa
и -XX:TieredStopAtLevel=1
помогли оптимизировать потребление памяти, но не улучшили пропускную способность и время отклика. Но у HotSpot широкий выбор сборщиков мусора (GC), и мы можем выбрать наиболее оптимальный для наших нужд. В данном случае мы переключились на Serial GC (флаг -XX:+UseSerialGC
), у которого оверхед меньше. Serial GC — самая простая реализация сборщика мусора. Он работает с одним потоком, тем самым снижая потребление памяти. Лучше всего он подходит для однопроцессорных машин или приложений с небольшими кучами.
Для снижения потребления памяти мы также установили параметр InitialHeapSize на 80 Мб (флаг -Xms80m
).
Конфигурации запуска:
- OpenJ9 (1) (размер кучи 1 Гб): -Xmx1G
- OpenJ9 (2) (размер кучи 1 Гб, кеш классов): -Xmx1G -Xshareclasses:name=mvn
- OpenJ9 (3) (размер кучи 1 Гб, кеш классов, быстрая компиляция): -Xmx1G -Xshareclasses:name=mvn -Xtune:virtualized -Xscmx200m
- HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб): -Xmx1G -Xms80m
- HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, AppCDS, C1): -Xmx1G -Xms80m -XX:SharedArchiveFile=app-cds.jsa -XX:TieredStopAtLevel=1
- HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, SerialGC): -Xmx1G -Xms80m -XX:SharedArchiveFile=app-cds.jsa -XX:TieredStopAtLevel=1 -XX:+UseSerialGC
Время отклика (~99,998%) | |||||
---|---|---|---|---|---|
OpenJ9 (1) (размер кучи 1 Гб) | OpenJ9 (2) (размер кучи 1 Гб, кеш классов) | OpenJ9 (3) (размер кучи 1 Гб, кеш классов, быстрая компиляция) | HotSpot (максимальный размер кучи 1 Гб, initial heap 80 MB) | HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, SerialGC) | HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, AppCDS, C1) |
2091 мс | 386 мс | 641 мс | 120 мс | 123 мс | 1852 мс |
Показатели измерялись на ~99,998% процентилей. На графике видно, что у дефолтной конфигурации HotSpot один из лучших показателей (lib-def-xms80m), но параметры -XX:SharedArchiveFile=app-cds.jsa
и -XX:TieredStopAtLevel=1
дали противоположный результат (lib-xms80m). Но даже у этой конфигурации время отклика было меньше, чем у OpenJ9 (1), показавшей худшие результаты. При этом HotSpot с SerialGC (lib-xms80m-serialgc) продемонстрировала лучшие показатели пропускной способности и потребления памяти.
Пропускная способность | |||||
---|---|---|---|---|---|
OpenJ9 (1) (размер кучи 1 Гб) | OpenJ9 (2) (размер кучи 1 Гб, кеш классов) | OpenJ9 (3) (размер кучи 1 Гб, кеш классов, быстрая компиляция) | HotSpot (максимальный размер кучи 1 Гб, initial heap 80 MB) | HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, SerialGC) | HotSpot (максимальный размер кучи 1 Гб, начальный размер кучи 80 Мб, AppCDS, C1) |
854,1 tps | 779,0 tps | 758,5 tps | 897,9 tps | 592,9 tps | 594,1 tps |
На графике выше видно, что дефолтная HotSpot со сборщиком мусора G1 обеспечивает лучшую пропускную способность, но потребляет больше памяти. Для улучшения показателей времени отклика или потребления памяти можно выбрать конфигурацию с Serial GC или AppCDS. Также можно использовать Parallel GC, который работает с несколькими потоками при сборке мусора и помогает улучшить пропускную способность.
Заключение
Результаты тестирования были схожими на всех платформах и показали, что HotSpot с дополнительными параметрами сопоставима с OpenJ9 или превосходит её. Это значит, что сам по себе выбор виртуальной машины не является гарантией быстродействия, ключевой фактор — это ее правильные настройки.
Дефолтная HotSpot обеспечивает меньшее время отклика и лучшую пропускную способность, чем OpenJ9 на сервере, что будет способствовать снижению затрат в долгосрочной перспективе. Значительное потребление памяти этой виртуальной машиной можно сократить, перейдя на другую ОС (Alpine Linux требует в два раза меньше оперативной памяти, чем CentOS) или внедрив в проект технологию нативных образов.
Что касается последней, Axiom JDK разработало Инструментарий Нативных Образов Axiom NIK, основанный на GraalVM CE и помогающий ускорить запуск приложений и снизить потребление памяти.
Подытоживая, любую JVM можно настроить под нужды проекта. Это значит, что для достижения хороших показателей у вас нет необходимости пользоваться только OpenJ9. Вы не потеряете, а возможно, и выиграете в производительности, выбрав в качестве корпоративной JDK доверенную среду разработки Axiom JDK Pro с российской техподдержкой и используя стандартные настройки лежащей в основе HotSpot для достижения своих целей.