Виртуальная машина Java - настройка GC
В предыдущей главе мы узнали о различных Gcs поколений. В этой главе мы обсудим, как настроить ГХ.
Размер кучи
Размер кучи - важный фактор производительности наших приложений Java. Если он слишком мал, он будет часто заполняться, и в результате сборщику мусора придется часто собирать его. С другой стороны, если мы просто увеличим размер кучи, хотя ее нужно будет собирать реже, длина пауз увеличится.
Кроме того, увеличение размера кучи серьезно сказывается на базовой ОС. Используя разбиение на страницы, ОС заставляет наши прикладные программы видеть намного больше памяти, чем фактически доступно. ОС управляет этим, используя некоторое пространство подкачки на диске, копируя на него неактивные части программ. Когда эти части необходимы, ОС копирует их обратно с диска в память.
Предположим, что машина имеет 8 ГБ памяти, а JVM видит 16 ГБ виртуальной памяти, JVM не будет знать, что на самом деле в системе доступно только 8 ГБ. Он просто запросит у ОС 16 ГБ, и как только получит эту память, он продолжит ее использовать. Операционной системе придется менять местами большое количество данных, и это сильно снижает производительность системы.
А затем наступают паузы, которые происходят во время полной сборки мусора такой виртуальной памяти. Поскольку сборщик мусора будет воздействовать на всю кучу для сбора и сжатия, ему придется долго ждать, пока виртуальная память будет выгружена с диска. В случае параллельного сборщика фоновым потокам придется много ждать, пока данные будут скопированы из области подкачки в память.
Итак, здесь возникает вопрос, как нам выбрать оптимальный размер кучи. Первое правило - никогда не запрашивать у ОС больше памяти, чем есть на самом деле. Это полностью предотвратило бы проблему частой замены. Если на машине установлено и запущено несколько JVM, то общий объем запросов к памяти по всем из них, вместе взятым, будет меньше, чем фактическое ОЗУ, присутствующее в системе.
Вы можете контролировать размер запроса памяти JVM, используя два флага:
-XmsN - Управляет запрошенной начальной памятью.
-XmxN - Контролирует максимальный объем запрошенной памяти.
Значения по умолчанию обоих этих флагов зависят от базовой ОС. Например, для 64-битных JVM, работающих в MacOS, -XmsN = 64M и -XmxN = минимум 1 ГБ или 1/4 от общей физической памяти.
Обратите внимание, что JVM может автоматически переключаться между двумя значениями. Например, если он замечает, что происходит слишком много сборки мусора, он будет продолжать увеличивать размер памяти до тех пор, пока он меньше -XmxN и желаемые цели производительности достигнуты.
Если вы точно знаете, сколько памяти требуется вашему приложению, вы можете установить -XmsN = -XmxN. В этом случае JVM не нужно определять «оптимальное» значение кучи, и, следовательно, процесс сборки мусора становится немного более эффективным.
Размеры поколений
Вы можете решить, какую часть кучи вы хотите выделить для YG, и сколько из нее вы хотите выделить для OG. Оба эти значения влияют на производительность наших приложений следующим образом.
Если размер YG очень большой, то его собирают реже. Это приведет к тому, что меньшее количество объектов будет продвинуто в OG. С другой стороны, если вы слишком сильно увеличите размер OG, то сбор и сжатие займет слишком много времени, и это приведет к длительным паузам STW. Таким образом, пользователь должен найти баланс между этими двумя значениями.
Ниже приведены флаги, которые вы можете использовать для установки этих значений:
-XX:NewRatio=N: Отношение YG к OG (значение по умолчанию = 2)
-XX:NewSize=N: Начальный размер YG
-XX:MaxNewSize=N: Максимальный размер YG
-XmnN: Установите для NewSize и MaxNewSize одно и то же значение, используя этот флаг
Начальный размер YG определяется значением NewRatio по заданной формуле -
(total heap size) / (newRatio + 1)
Поскольку начальное значение newRatio равно 2, приведенная выше формула дает начальное значение YG, равное 1/3 от общего размера кучи. Вы всегда можете переопределить это значение, явно указав размер YG с помощью флага NewSize. Этот флаг не имеет значения по умолчанию, и если он не установлен явно, размер YG будет продолжать вычисляться с использованием приведенной выше формулы.
Permagen и Metaspace
Пермаген и метапространство - это области кучи, где JVM хранит метаданные классов. Пространство называется «пермаген» в Java 7, а в Java 8 - «метапространством». Эта информация используется компилятором и средой выполнения.
Вы можете контролировать размер пермагена, используя следующие флаги: -XX: PermSize=N и -XX:MaxPermSize=N. Размер Metaspace можно контролировать с помощью:-XX:Metaspace- Size=N и -XX:MaxMetaspaceSize=N.
Есть некоторые различия в том, как управляются пермаген и метапространство, когда значения флага не установлены. По умолчанию оба имеют начальный размер по умолчанию. Но хотя метапространство может занимать столько кучи, сколько необходимо, пермаген может занимать не больше, чем исходные значения по умолчанию. Например, JVM 64b имеет 82M кучи как максимальный размер permagen.
Обратите внимание, что поскольку метапространство может занимать неограниченное количество памяти, если не указано иное, может возникнуть ошибка нехватки памяти. Полная сборка мусора выполняется всякий раз, когда размер этих регионов изменяется. Следовательно, во время запуска, если загружается много классов, метапространство может продолжать изменять размер, в результате чего каждый раз создается полный сборщик мусора. Таким образом, запуск больших приложений занимает много времени, если начальный размер метапространства слишком мал. Рекомендуется увеличить начальный размер, поскольку это сокращает время запуска.
Хотя пермаген и метапространство содержат метаданные класса, они не являются постоянными, и пространство восстанавливается сборщиком мусора, как в случае с объектами. Обычно это бывает с серверными приложениями. Каждый раз, когда вы выполняете новое развертывание на сервере, старые метаданные должны быть очищены, поскольку теперь загрузчикам новых классов потребуется место. Это пространство освобождает сборщик мусора.