Модель памяти Java (JVM) – Управление памятью в Java

Понимание Модели памяти JVM, Управления памятью в Java очень важно, если вы хотите понять работу Сборщика мусора в Java. Сегодня мы рассмотрим управление памятью в Java, различные части памяти JVM и как отслеживать и настраивать сборку мусора.

Модель памяти Java (JVM)

Как видно на изображении выше, память JVM разделена на отдельные части. На широком уровне память кучи JVM физически разделена на две части – Молодое поколение и Старое поколение.

Управление памятью в Java – Молодое поколение

Молодое поколение – это место, где создаются все новые объекты. Когда молодое поколение заполняется, выполняется сборка мусора. Эта сборка мусора называется Минорная GC. Молодое поколение делится на три части – Память Эдема и два Пространства Долгожителей. Важные моменты о пространствах молодого поколения:

  • Большинство новых объектов находятся в пространстве памяти Eden.
  • Когда пространство Eden заполняется объектами, выполняется Минорная сборка мусора, и все выжившие объекты перемещаются в одно из пространств для выживших объектов.
  • Минорная сборка мусора также проверяет выжившие объекты и перемещает их в другое пространство для выживших объектов. Таким образом, в любой момент времени одно из пространств для выживших объектов всегда пусто.
  • Объекты, которые выжили после множества циклов сборки мусора, перемещаются в пространство памяти старшего поколения. Обычно это делается путем установки порога для возраста объектов молодого поколения, прежде чем они станут пригодными для перемещения в старшее поколение.

Управление памятью в Java – Поколение Old

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

Событие “Остановка мира”

Все сборки мусора являются “Остановками мира”, потому что все потоки приложения останавливаются до завершения операции. Поскольку молодое поколение содержит объекты с коротким сроком жизни, минорная сборка мусора происходит очень быстро, и приложение не замедляется из-за этого. Однако основная сборка мусора занимает много времени, потому что проверяет все живые объекты. Основную сборку мусора следует минимизировать, потому что она делает ваше приложение нереагирующим на время сборки мусора. Поэтому, если у вас отзывчивое приложение, и происходит много основных сборок мусора, вы заметите ошибки тайм-аута. Время, затраченное сборщиком мусора, зависит от стратегии, используемой для сборки мусора. Поэтому необходимо отслеживать и настраивать сборщик мусора, чтобы избежать ошибок времени ожидания в высокоотзывчивых приложениях.

Модель памяти Java – Постоянное поколение

Постоянное поколение или “Perm Gen” содержит метаданные приложения, необходимые JVM для описания классов и методов, используемых в приложении. Обратите внимание, что Perm Gen не является частью кучи памяти Java. Perm Gen заполняется JVM во время выполнения на основе классов, используемых приложением. Perm Gen также содержит классы и методы библиотеки Java SE. Объекты Perm Gen собираются мусором в полной сборке мусора.

Модель памяти Java – Область методов

Область методов является частью пространства в Perm Gen и используется для хранения структуры класса (временные константы и статические переменные) и кода для методов и конструкторов.

Модель памяти Java – Пул памяти

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

Модель памяти Java – Пул временных констант

Пул временных констант – это представление времени выполнения на уровне класса постоянного пула в классе. Он содержит временные константы класса и статические методы. Пул временных констант является частью области методов.

Модель памяти Java – Стек памяти Java

Стековая память Java используется для выполнения потока. В ней содержатся значения, специфичные для метода, которые имеют недолговечность, а также ссылки на другие объекты в куче, на которые ссылается метод. Вы должны прочитать Разница между памятью стека и кучи.

Управление памятью в Java – Переключатели кучи памяти Java

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

VM Switch VM Switch Description
-Xms For setting the initial heap size when JVM starts
-Xmx For setting the maximum heap size.
-Xmn For setting the size of the Young Generation, rest of the space goes for Old Generation.
-XX:PermGen For setting the initial size of the Permanent Generation memory
-XX:MaxPermGen For setting the maximum size of Perm Gen
-XX:SurvivorRatio For providing ratio of Eden space and Survivor Space, for example if Young Generation size is 10m and VM switch is -XX:SurvivorRatio=2 then 5m will be reserved for Eden Space and 2.5m each for both the Survivor spaces. The default value is 8.
-XX:NewRatio For providing ratio of old/new generation sizes. The default value is 2.

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

Управление памятью в Java – Сборка мусора в Java

Сборка мусора в Java – это процесс идентификации и удаления неиспользуемых объектов из памяти и освобождения места для выделения объектов, созданных в будущем при обработке. Одной из лучших особенностей языка программирования Java является автоматическая сборка мусора, в отличие от других языков программирования, таких как C, где выделение и освобождение памяти является ручным процессом. Сборщик мусора – это программа, работающая в фоновом режиме, которая анализирует все объекты в памяти и находит объекты, на которые нет ссылок ни от одной части программы. Все эти объекты без ссылок удаляются, и место освобождается для выделения другим объектам. Одним из основных способов сборки мусора является три шага:

  1. Маркировка: Это первый шаг, на котором сборщик мусора определяет, какие объекты используются, а какие нет.
  2. Обычное удаление: Сборщик мусора удаляет неиспользуемые объекты и освобождает свободное место для выделения другим объектам.
  3. Удаление с компактированием: Для повышения производительности после удаления неиспользуемых объектов все выжившие объекты могут быть перемещены вместе. Это увеличит производительность выделения памяти для новых объектов.

Существуют две проблемы с простым подходом маркировки и удаления.

  1. Первая проблема заключается в том, что это неэффективно, потому что большинство только что созданных объектов станут неиспользуемыми
  2. . Во-вторых, объекты, используемые в течение нескольких циклов сборки мусора, скорее всего, будут использоваться и в будущих циклах.

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

Управление памятью в Java – типы сборки мусора в Java

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

  1. Серийная сборка мусора (-XX:+UseSerialGC): Серийная сборка мусора использует простой подход пометки-удаления-уплотнения для сборки мусора молодого и старого поколений, то есть минорной и мажорной сборки мусора. Серийная сборка мусора полезна на клиентских машинах, таких как наши простые автономные приложения, и на машинах с меньшим процессором. Она хороша для небольших приложений с низким объемом памяти.
  2. Параллельный GC (-XX:+UseParallelGC): Параллельный GC такой же, как и последовательный GC, за исключением того, что он создает N потоков для сборки мусора молодого поколения, где N – количество ядер ЦП в системе. Мы можем контролировать количество потоков, используя параметр JVM -XX:ParallelGCThreads=n. Параллельный сборщик мусора также называется сборщиком пропускной способности, потому что он использует несколько ЦП для увеличения производительности сборки мусора. Параллельный GC использует один поток для сборки мусора в старом поколении.
  3. Параллельный старый GC (-XX:+UseParallelOldGC): Это то же самое, что и параллельный GC, за исключением того, что он использует несколько потоков как для сборки мусора молодого поколения, так и для сборки мусора старого поколения.
  4. Сборщик Concurrent Mark Sweep (CMS) (-XX:+UseConcMarkSweepGC): Сборщик CMS также называется сборщиком с низкими паузами во время выполнения. Он выполняет сборку мусора для старого поколения. Сборщик CMS пытается минимизировать паузы из-за сборки мусора, выполняя большую часть работы по сборке мусора параллельно с потоками приложения. Сборщик CMS в молодом поколении использует тот же алгоритм, что и параллельный сборщик. Этот сборщик мусора подходит для отзывчивых приложений, где нельзя позволить себе длительные времена паузы. Мы можем ограничить количество потоков в сборщике CMS, используя параметр JVM -XX:ParallelCMSThreads=n.
  5. Сборщик мусора G1 (-XX:+UseG1GC): Сборщик мусора Garbage First или G1 доступен начиная с Java 7, его долгосрочная цель – заменить сборщик CMS. Сборщик G1 – это параллельный, конкурентный и инкрементально компактирующий сборщик мусора с низким временем простоя. Сборщик Garbage First не работает как другие сборщики, и здесь нет концепции молодого и старого поколений. Он делит пространство кучи на несколько равных по размеру регионов кучи. При вызове сборки мусора он сначала собирает регион с меньшим количеством активных данных, отсюда и название “Garbage First”. Больше подробностей можно найти в документации Oracle по сборщику Garbage-First.

Управление памятью в Java – мониторинг сборки мусора в Java

Мы можем использовать командную строку Java, а также инструменты пользовательского интерфейса для мониторинга действий сборки мусора приложения. В качестве примера я использую одно из демонстрационных приложений, предоставленных загрузками Java SE. Если вы хотите использовать то же самое приложение, перейдите на страницу Загрузки Java SE и загрузите JDK 7 и JavaFX демонстрации и образцы. Пример приложения, который я использую, называется Java2Demo.jar и находится в каталоге jdk1.7.0_55/demo/jfc/Java2D. Однако это необязательный шаг, и вы можете запустить команды мониторинга GC для любого Java-приложения. Используемая мной команда для запуска демонстрационного приложения:

pankaj@Pankaj:~/Downloads/jdk1.7.0_55/demo/jfc/Java2D$ java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar

jstat

Мы можем использовать инструмент командной строки jstat для мониторинга памяти JVM и действий по сборке мусора. Он поставляется вместе со стандартным JDK, поэтому вам не нужно ничего дополнительно устанавливать. Для выполнения jstat вам нужно знать идентификатор процесса приложения, вы можете легко получить его, используя команду ps -eaf | grep java.

pankaj@Pankaj:~$ ps -eaf | grep Java2Demo.jar
  501 9582  11579   0  9:48PM ttys000    0:21.66 /usr/bin/java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar
  501 14073 14045   0  9:48PM ttys002    0:00.00 grep Java2Demo.jar

Итак, идентификатор процесса для моего java-приложения – 9582. Теперь мы можем запустить jstat команду, как показано ниже.

pankaj@Pankaj:~$ jstat -gc 9582 1000
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
1024.0 1024.0  0.0    0.0    8192.0   7933.3   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8026.5   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8030.0   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8122.2   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8171.2   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  48.7   0.0    8192.0   106.7    42108.0    23401.3   20480.0 19990.9    158    0.275  40      1.381    1.656
1024.0 1024.0  48.7   0.0    8192.0   145.8    42108.0    23401.3   20480.0 19990.9    158    0.275  40      1.381    1.656

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

  • S0C и S1C: Этот столбец показывает текущий размер областей Survivor0 и Survivor1 в KB.
  • S0U и S1U: Этот столбец показывает текущее использование областей Survivor0 и Survivor1 в KB. Обратите внимание, что одна из областей выживших всегда пуста.
  • EC и EU: Эти столбцы показывают текущий размер и использование пространства Eden в KB. Обратите внимание, что размер EU увеличивается, и как только он превысит EC, вызывается минорная GC, и размер EU уменьшается.
  • OC и OU: Эти столбцы показывают текущий размер и текущее использование старшего поколения в KB.
  • PC и PU: Эти столбцы показывают текущий размер и текущее использование Perm Gen в KB.
  • YGC и YGCT: Столбец YGC отображает количество событий GC, произошедших в молодом поколении. Столбец YGCT отображает накопленное время для операций GC для молодого поколения. Обратите внимание, что оба значения увеличиваются в том же ряду, где значение EU уменьшается из-за минорной GC.
  • FGC и FGCT: Столбец FGC отображает количество событий полной GC. Столбец FGCT отображает накопленное время для операций полной GC. Обратите внимание, что время полной GC слишком высоко по сравнению с временами GC молодого поколения.
  • GCT: Этот столбец отображает общее накопленное время для операций GC. Обратите внимание, что это сумма значений столбцов YGCT и FGCT.

Преимущество jstat заключается в том, что его можно выполнять на удаленных серверах, где нет графического интерфейса. Обратите внимание, что сумма S0C, S1C и EC составляет 10m, как указано через опцию JVM -Xmn10m.

Java VisualVM с Visual GC

Если вы хотите видеть операции с памятью и сборку мусора в графическом интерфейсе, то вы можете использовать инструмент jvisualvm. Java VisualVM также является частью JDK, поэтому вам не нужно загружать его отдельно. Просто запустите команду jvisualvm в терминале, чтобы запустить приложение Java VisualVM. После запуска вам нужно установить плагин Visual GC из меню Инструменты – Плагины, как показано на изображении ниже. После установки Visual GC просто откройте приложение в левой колонке и перейдите в раздел Visual GC. Вы получите изображение памяти JVM и детали сборки мусора, как показано на изображении ниже.

Настройка сборки мусора в Java

Настройка сборки мусора в Java должна быть последним вариантом, который вы должны использовать для увеличения пропускной способности вашего приложения, и только в том случае, если вы видите снижение производительности из-за увеличения времени сборки мусора, вызывающего превышение времени ожидания приложения. Если вы видите ошибки java.lang.OutOfMemoryError: PermGen space в журналах, то попробуйте отслеживать и увеличивать пространство памяти Perm Gen, используя параметры JVM -XX:PermGen и -XX:MaxPermGen. Вы также можете попробовать использовать -XX:+CMSClassUnloadingEnabled и проверить, как он работает с сборщиком мусора CMS. Если вы видите много операций Full GC, то попробуйте увеличить пространство памяти для старого поколения. В целом, настройка сборки мусора требует много усилий и времени, и нет жестких правил для этого. Вам придется попробовать различные варианты и сравнить их, чтобы найти наилучший подходящий для вашего приложения. Это все, что касается модели памяти Java, управления памятью в Java и сборки мусора. Надеюсь, это поможет вам понять процесс памяти JVM и сборки мусора.

Source:
https://www.digitalocean.com/community/tutorials/java-jvm-memory-model-memory-management-in-java