Рубрики
Без рубрики

Краткий обзор сборки мусора Java: что это такое, и Как это работает

В этом уроке мы поговорим о том, как работают различные сборщики мусора Java и что вы можете использовать… Помеченный java, gc, наблюдаемость, производительность.

Понимание Сборки Мусора Java (Серия из 3 Частей)

В этом уроке мы поговорим о том, как работают различные сборщики мусора Java и чего вы можете от них ожидать. Это даст нам необходимую информацию, чтобы начать настройку выбранного вами алгоритма сбора мусора.

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

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

Давайте быстро рассмотрим, когда объект готов к сбору мусора и как на самом деле запросить виртуальную машину Java для запуска сбора мусора.

Как сделать Объект Подходящим для GC?

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

public Integer run() {
  Integer variableOne = 10;
  Integer variableTwo = 20;
  return variableOne + variableTwo;
}

В методе run() мы явно создаем две переменные. Они сначала складываются в кучу, в кучу молодого поколения. Как только метод завершает свое выполнение, они больше не нужны, и они начинают иметь право на сбор мусора. Когда происходит сборка мусора молодого поколения, память, используемая этими переменными, может быть восстановлена. Если это произойдет, ранее занятая память будет видна как свободная.

Как запросить JVM для запуска GC?

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

Если вы хотите принудительно собрать мусор, вы можете использовать системный объект из пакета java.lang и его gc() метод или Runtime.getRuntime().gc() вызов. Как указано в документации – Виртуальная машина Java приложит все усилия, чтобы освободить место. Это означает, что сборка мусора на самом деле может не произойти, это зависит от JVM. Если произойдет сборка мусора, это будет крупная сборка, что означает, что мы можем ожидать, что произойдет событие остановить мир . В общем, использование System.gc() считается плохой практикой, и мы должны настроить работу сборщика мусора вместо того, чтобы вызывать его явно.

Независимо от того, какую реализацию сборщика мусора мы используем, для очистки памяти необходима короткая пауза. Эти паузы также называются событиями “остановить мир” или, короче говоря, STW. Вы можете представить рабочие циклы вашего приложения на основе JVM следующим образом:

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

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

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

  • Молодые данные долго не протянут
  • Старые данные будут продолжать сохраняться в памяти

Вот почему память кучи JVM разделена на поколения:

  • Молодое поколение которое разделено на две секции под названием Пространство Эдема и Пространство выживших
  • Старое поколение, или Арендованное пространство .

Упрощенное продвижение объектов между пространствами и поколениями можно проиллюстрировать следующим примером. Когда объект создается, он сначала помещается в пространство молодое поколение в пространство Эдем . Как только происходит сбор мусора, объект перемещается в Пространство выживших 0 и далее в Пространство выживших 1 . Если объект все еще используется в этот момент, следующий цикл сборки мусора переместит его в Арендованное пространство , что означает, что он перемещен в старое поколение . Вы можете представить это следующим образом:

Так в Эдем пространство содержит вновь созданные объекты и пусто в начале эпохи . В течение эпохи пространство Eden будет заполняться, в конечном итоге вызывая Незначительное событие GC при заполнении. Оставшиеся в живых пространства содержат объекты, которые использовались по крайней мере в течение одной эпохи . Объекты, которые пережили многие эпохи , в конечном итоге будут переведены в Постоянное поколение.

До Java 8 существовало одно дополнительное пространство памяти, называемое PermGen . PermGen или иным образом Постоянное поколение было особым пространством на куче, отделенным от других ее частей – молодого и старшего поколения. Он использовался для хранения метаданных, таких как классы и методы.

Начиная с Java 8, метапространство – это пространство памяти, которое заменяет удаленное пространство PermGen. Реализация отличается от PermGen, и это пространство кучи теперь автоматически изменяется, ограничивая проблемы, связанные с нехваткой памяти в этой области кучи. Память метапространства может быть собрана как мусор, и классы, которые больше не используются, могут быть очищены, когда Метапространство достигнет своего максимального размера.

Существует несколько флагов, которые можно использовать для управления размером и поведением пространства памяти метапространства:

  • -XX: Размер метапространства – начальный размер области памяти метапространства,
  • – XX:MaxMetaspaceSize – максимальный размер памяти и области метапространства,
  • -XX: minmetaspacefreeratio – минимальный процент емкости метаданных класса, который должен быть свободен после сборки мусора,
  • -XX: maxmetaspacefreeratio – максимальный процент емкости метаданных класса, который должен быть свободен после сборки мусора.

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

Структура кучи сборщика мусора G1

То, что мы написали выше, справедливо для всех сборщиков мусора, включая последовательную, параллельную и параллельную очистку меток. Мы обсудим их немного позже. Однако сборщик мусора G1 делает еще один шаг вперед и делит кучу на нечто, называемое регионами . регион – это небольшая независимая куча, которая может быть динамически настроена на тип Eden, Survivor или Tenured:

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

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

Размер области при использовании G1GC можно регулировать. Если размер кучи установлен ниже 4 ГБ, размер области будет автоматически установлен на 1 МБ. Для кучи от 4 до 8 ГБ размер области будет установлен в 2 МБ и так далее, до 32 МБ размер области для кучи размером 64 ГБ или больше. Как правило, размер региона должен быть в степени два и составлять от 1 до 32 МБ. По умолчанию JVM попытается настроить оптимальное количество из двух тысяч регионов или более во время запуска приложения. Мы можем контролировать это с помощью параметра JVM.

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

Теперь вы можете задаться вопросом, что запускает сбор мусора, и это был бы отличный вопрос. Распространенными триггерами для сборки мусора являются заполненное пространство Eden, недостаточно свободного места для размещения объекта, внешние ресурсы, такие как System.gc() , инструменты, такие как jmap, или недостаточно свободного места для создания объекта.

Чтобы все было еще сложнее, существует несколько типов событий по сбору мусора. Вы можете разделить их очень упрощенным способом, следующим образом:

  • Незначительное событие – происходит, когда пространство Эдема заполнено и данные перемещены в пространство выживших. Таким образом, в молодом поколении происходит незначительное событие
  • Смешанное событие – незначительное событие плюс возвращение занятого поколения
  • Полный GC мероприятие – совместная расчистка пространства для молодого и старого поколения

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

Следующее, что я хотел бы упомянуть, – это огромный объект. Помнишь? Это те, которые больше, чем одна область в нашей куче, когда мы имеем дело со сборщиком мусора G1 (G1GC). На самом деле, любой объект, размер которого превышает 50 % от размера региона, считается огромным. Эти объекты не выделяются в пространстве для молодого поколения, но вместо этого они помещаются непосредственно в поколение, состоящее в штате. Такие объекты могут увеличить время паузы сборщика мусора и увеличить риск запуска полной сборки мусора из-за нехватки свободного места.

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

Тем не менее, в Java существует пять типов сборщиков мусора:

Серийный Сборщик Мусора

Последовательный сборщик мусора предназначен предназначен|/для использования в однопоточных средах . Перед выполнением сборки мусора этот сборщик мусора замораживает все потоки приложений . Из-за этого он не подходит для многопоточных сред, таких как серверные приложения. Тем не менее, он идеально подходит для однопоточных приложений, которые не требуют малого времени паузы. Например, пакетные задания.

В документации по сборщикам мусора Java также упоминается, что этот сборщик мусора может быть полезен на многопроцессорных машинах для приложений с набором данных примерно до 100 МБ.

Параллельный Сборщик Мусора

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

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

Сборщик мусора с Одновременной Разметкой

Сборщик мусора Параллельная очистка меток (CMS) является одной из реализаций, которые называются в основном параллельными . Они выполняют дорогостоящие операции с использованием нескольких потоков . Они также делятся потоки , используемые для сбора мусора с приложением . Накладные расходы для этого типа сборки мусора обусловлены не только тем фактом, что они выполняют сборку одновременно, но и тем, что параллельная сборка должна быть включена.

CMS GC предназначена для приложений, которые предпочитают короткие паузы. В принципе, он работает медленнее по сравнению с параллельным или последовательным сборщиком мусора, но ему не нужно останавливать потоки приложения для выполнения сборки мусора.

Этот сборщик мусора следует выбирать, если ваше приложение предпочитает короткие паузы и может позволить себе совместно использовать потоки приложения со сборщиком мусора. Имейте в виду, однако, что сборщик мусора с одновременной разверткой меток должен быть удален в Java 14, и вам следует взглянуть на сборщик мусора G1, если вы его еще не используете.

Сборщик мусора G1

Сборщик мусора G1 – это алгоритм сбора мусора, который был представлен с 4-м обновлением 7-й версии Java и с тех пор улучшен. G1GC был разработан с низкой задержкой, но за это приходится платить – более частая работа, что означает больше циклов процессора, затрачиваемых на сборку мусора. Он разбивает кучу на более мелкие области, что облегчает сбор мусора и очистку памяти в стиле эвакуации . Это означает, что объекты перемещаются из очищенной области и копируются в другую область. Большая часть сбора мусора производится в молодом поколении, где это наиболее эффективно.

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

Подумайте об этом так – если у вас есть службы, которые чувствительны к задержке G1 сборщик мусора может быть очень хорошим выбором . Низкие задержки означают, что эти службы не пострадают от длительных событий, приводящих к остановке. Конечно, за счет более высокой загрузки процессора . Кроме того, сборщик мусора G1 был разработан для работы с большими размерами кучи – если у вас куча больше 32 ГБ, G1 обычно является хорошим выбором. Сборщик мусора G1 является заменой сборщика мусора CMS, а также сборщиком мусора по умолчанию в самых последних версиях Java.

Z Сборщик мусора

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

Ожидается, что ZGC будет хорошо работать с приложениями, требующими пауз 10 мс или менее, и с приложениями, в которых используются очень большие кучи.

Существует множество преимуществ сборки мусора в Java. Однако главным из них, о котором вы, возможно, не подумаете в первую очередь, является упрощенный код . Нам не нужно беспокоиться о правильном назначении памяти и циклах освобождения. В нашем коде мы просто прекращаем использовать объект, и память, которую он использует, будет автоматически восстановлена в какой-то момент. Это еще одна дополнительная ценность с точки зрения простоты. Процесс восстановления памяти происходит автоматически и является задачей внутреннего алгоритма внутри JVM. Мы просто контролируем, какой алгоритм мы хотим использовать – если мы хотим его контролировать. Конечно, мы все еще можем столкнуться с утечками памяти, если сохраним ссылки на объекты навсегда, но это другая пара обуви.

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

Мы рассмотрим процесс настройки сборки мусора в следующем посте серии, но до этого мы хотели поделиться некоторыми хорошими и плохими практиками в области сборки мусора. Прежде всего – вам следует избегать вызова метода System.gc() для запроса явной сборки мусора. Как мы уже упоминали, это считается плохой практикой, и ее следует избегать.

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

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

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

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

Понимание Сборки Мусора Java (Серия из 3 Частей)

Оригинал: “https://dev.to/sematext/a-quick-start-on-java-garbage-collection-what-it-is-and-how-it-works-14d9”