Настройка производительности JVM для больших приложений - PullRequest
31 голосов
/ 19 февраля 2009

Параметры JVM по умолчанию не оптимальны для запуска больших приложений. Любые идеи от людей, которые настроили его на реальном приложении, были бы полезны. Мы запускаем приложение на 32-разрядной машине с Windows, где клиентская JVM используется по умолчанию . Мы добавили -server и изменили NewRatio на 1: 3 (молодое поколение).

Какие-либо другие параметры / настройки, которые вы пробовали и нашли полезными?

[Обновить] Конкретным типом приложения, о котором я говорю, является серверное приложение, которое редко закрывается и занимает не менее -Xmx1024m. Также предположим, что приложение уже профилировано. Я ищу общие рекомендации, касающиеся производительности JVM * только 1008 *.

Ответы [ 7 ]

27 голосов
/ 26 мая 2016

Предисловие

Фон

Был в магазине Java. Целые месяцы были посвящены проведению тестов производительности в распределенных системах, основные приложения были на Java Некоторые из которых подразумевают продукты, разработанные и продаваемые самим Sun (тогда Oracle).

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

В мире Java все быстро меняется, поэтому его часть может быть уже устаревшей с прошлого года, когда я все это делал. (Java 10 уже вышла?)

Хорошая практика

Что вы ДОЛЖНЫ делать: эталон, эталон, ЭТАЛОН!

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

Также, вы должны следить за JVM. Включить мониторинг. Хорошие приложения обычно предоставляют веб-страницу мониторинга и / или API. В противном случае есть общие инструменты Java (JVisualVM, JMX, hprof и некоторые флаги JVM).

Имейте в виду, что при настройке JVM производительность обычно не увеличивается. Это скорее "сбой или не сбой, найти точку перехода" . Речь идет о том, чтобы знать, что когда вы предоставляете такое количество ресурсов вашему приложению, вы можете последовательно ожидать , что количество исполнений взамен. Знание - сила.

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

Что вы будете делать большую часть времени: жить с надежными чувствительными значениями по умолчанию

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

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

Затем вы можете настроить приложение: JAVA_OPTS: -server -Xms???g -Xmx???g

  • -server: включить полную оптимизацию (в настоящее время этот флаг является автоматическим на большинстве JVM)
  • -Xms -Xmx: установите минимальную и максимальную кучу (всегда одинаковое значение для обоих, это единственная оптимизация, которую нужно сделать).

Отлично, вы знаете обо всех параметрах оптимизации, которые нужно знать о JVM, поздравляем! Это было просто: D

Чего ВЫ НЕ ДОЛЖНЫ делать, КОГДА-ЛИБО:

Пожалуйста, НЕ копируйте случайные строки, которые вы нашли в Интернете, особенно если они занимают несколько строк, например:

-server  -Xms1g -Xmx1g  -XX:PermSize=1g -XX:MaxPermSize=256m  -Xmn256m -Xss64k  -XX:SurvivorRatio=30  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark  -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof   -Dcom.sun.management.jmxremote.port=5616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

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

Фактическая настройка

Как выбрать размер памяти:

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

Важное примечание : Процесс java займет до max heap PLUS 10% . Издержки X% - это управление кучей, не включенное в саму кучу.

Вся память обычно предварительно выделяется процессом при запуске. Вы можете увидеть процесс, используя максимальную кучу ВСЕ ВРЕМЯ. Это просто неправда. Вам необходимо использовать инструменты мониторинга Java, чтобы увидеть, что на самом деле используется.

Нахождение нужного размера:

  • Если происходит сбой с OutOfMemoryException, недостаточно памяти
  • Если с OutOfMemoryException не произойдет сбой, значит, слишком много памяти
  • Если памяти слишком много, НО аппаратное обеспечение получило и / или уже оплачено, это совершенное число, работа выполнена!

JVM6 - бронза, JVM7 - ​​золото, JVM8 - платина ...

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

В ознакомительных целях. Это как минимум 4 доступных сборщика мусора в Oracle Java 7-8 (HotSpot) и OpenJDK 7-8. (Другие JVM могут быть совершенно другими, например, Android, IBM, встроенные):

  • SerialGC
  • ParallelGC
  • ConcurrentMarkSweepGC
  • G1GC
  • (плюс варианты и настройки)

[Начиная с Java 7 и далее. Код Oracle и OpenJDK частично используются совместно. GC должен быть (в основном) одинаковым на обеих платформах.]

JVM> = 7 имеют много оптимизаций и выбирают приличные значения по умолчанию. Это немного меняется в зависимости от платформы. Это балансирует несколько вещей. Например, решение включить многоядерные оптимизации или нет, если процессор имеет несколько ядер. Вы должны позволить этому сделать это. Не изменять и не форсировать настройки ГХ.

Это нормально, что компьютер принимает решение за вас (для этого и нужны компьютеры). Лучше иметь настройки JVM всегда оптимальными на 95%, чем принудительно устанавливать «всегда агрессивный сбор из 8 ядер для меньшего времени паузы» на всех блоках, половина из которых в конце равна t2.small.

Исключение : когда приложение поставляется с руководством по производительности и определенной настройкой на месте. Можно оставить все настройки как есть.

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

Особый случай: -XX: + UseCompressedOops

JVM имеет специальную настройку, которая вызывает внутреннее использование 32-битного индекса (читай: как указатели). Это позволяет адресовать 4 294 967 295 объектов * адрес 8 байтов => 32 ГБ памяти. (НЕ путать с адресным пространством 4 ГБ для указателей REAL).

Снижает общее потребление памяти с потенциальным положительным влиянием на все уровни кэширования.

Пример из реальной жизни : в документации ElasticSearch говорится, что работающий 32-битный узел 32 ГБ может быть эквивалентен 64-битному узлу 40 ГБ с точки зрения фактических данных, хранящихся в памяти.

Заметка по истории : Известно, что флаг был нестабильным в эпоху до Java-7 (возможно, даже до-Java-6). Некоторое время он отлично работал в новой JVM.

Повышение производительности виртуальной машины Java HotSpot ™

[...] В Java SE 7 использование сжатых опций является значением по умолчанию для 64-разрядных процессов JVM, когда -Xmx не указан и для значений -Xmx менее 32 гигабайт. Для JDK 6 до выпуска 6u23 используйте флаг -XX: + UseCompressedOops с командой java, чтобы включить эту функцию.

См. : И снова JVM загорается на годы вперед по сравнению с ручной настройкой. Тем не менее, об этом интересно знать =)

Особый случай: -XX: + UseNUMA

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

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

Совершенно очевидно, что доступ к данным в кеше L2 в текущем процессоре ОЧЕНЬ быстрее, чем полный переход к карте памяти из другого сокета.

Я полагаю, что все системы с несколькими сокетами , продаваемые сегодня, являются NUMA по дизайну, в то время как все потребительские системы НЕ. Проверьте, поддерживает ли ваш сервер NUMA с помощью команды numactl --show в Linux.

Флаг с поддержкой NUMA указывает JVM оптимизировать распределение памяти для базовой аппаратной топологии.

Увеличение производительности может быть значительным (то есть две цифры: + XX%). Фактически, кто-то, переключающийся с «NOT-NUMA 10CPU 100GB» на «NUMA 40CPU 400GB», может испытать [драматическую] потерю производительности, если он не знает о флаге.

Примечание : Есть обсуждения для определения NUMA и автоматической установки флага в JVM http://openjdk.java.net/jeps/163

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

Навстречу будущему: -XX: + UseG1GC

Последнее усовершенствование в сборке мусора - G1 сборщик (читай: Garbage First) .

Предназначен для систем с высоким ядром и высокой памятью. На абсолюте минимум 4 ядра + 6 ГБ памяти. Он предназначен для баз данных и приложений, интенсивно использующих память, используя в 10 раз больше.

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

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

Не трогать: Размеры генерации (NewGen, PermGen ...)

ГХ разбил память на несколько секций. (Не вдаваясь в подробности, вы можете гуглить "Java GC Generations".)

В последний раз я тратил неделю на то, чтобы попробовать 20 различных комбинаций флагов поколений в приложении со скоростью 10000 ударов в секунду. Я получал великолепное повышение от -1% до +1%.

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

Заключение

Надеюсь, это поможет вам. Веселитесь с JVM.

Java - лучший язык и лучшая платформа в мире! Иди распространяй любовь: D

17 голосов
/ 19 февраля 2009

Таких сведений много.

Сначала профилируйте код перед настройкой JVM.

Во-вторых, внимательно прочитайте документацию JVM ; вокруг много «городских легенд». Например, флаг -server помогает, только если JVM остается резидентной и работает в течение некоторого времени; -server «включает» JIT / HotSpot, и для его включения требуется много проходов по одному и тому же пути. -сервер, с другой стороны, замедляет начальное выполнение JVM, так как время установки больше.

Вокруг есть несколько хороших книг и сайтов. См. Например, http://www.javaperformancetuning.com/

7 голосов
/ 19 февраля 2009

Посмотрите здесь (или выполните поиск в Google для настройки точки доступа) http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

Вы определенно хотите профилировать свое приложение, прежде чем пытаться настроить виртуальную машину. В NetBeans встроен хороший профилировщик, который позволит вам видеть все виды вещей.

Однажды мне кто-то сказал, что GC не работает для их приложения - я посмотрел на код и обнаружил, что они никогда не закрывали результаты своих запросов к базе данных, поэтому они сохраняли огромное количество байтовых массивов. После того, как мы закрыли результаты, время перешло от более 20 минут и ГБ памяти к примерно 2 минутам и очень маленькому объему памяти. Они смогли удалить параметры настройки JVM, и все было хорошо.

1 голос
/ 16 октября 2012

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

  1. размеры памяти
  2. выбор коллекторов GC
  3. параметры, относящиеся к коллекторам ГХ
1 голос
/ 20 февраля 2009

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

В общем, профилирование вашего приложения имеет гораздо большее значение, чем аргументы JVM.

1 голос
/ 19 февраля 2009

Абсолютно лучший способ ответить на этот вопрос - провести контролируемое тестирование приложения в среде, максимально приближенной к «производственной», которую вы можете создать. Вполне возможно, что использование -server, разумный начальный размер кучи и относительно умное поведение последних JVM будут вести себя так же или лучше, чем подавляющее большинство настроек, которые обычно пробуют.

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

0 голосов
/ 19 февраля 2009

Это будет сильно зависеть от вашего приложения, поставщика и версии JVM. Вы должны четко понимать, что вы считаете проблемой производительности. Вы обеспокоены определенными критическими разделами кода? Вы уже профилировали приложение? JVM тратит слишком много времени на сбор мусора?

Я бы, вероятно, начал с опции -verbose: gc JVM, чтобы посмотреть, как работает сборщик мусора. Во многих случаях самое простое решение - просто увеличить максимальный размер кучи с помощью -Xmx. Если вы научитесь интерпретировать вывод -verbose: gc, он расскажет вам почти все, что вам нужно знать о настройке JVM в целом. Но выполнение этого само по себе не приведет к тому, что плохо настроенный код будет работать быстрее. Большинство параметров настройки JVM предназначены для повышения производительности сборщика мусора и / или размеров памяти.

Для профилирования мне нравится yourkit.com

...