Статьи

Сборщик мусора Bootcamp 1.0

Содержание

Что такое сборка мусора

Сборка мусора в Java — это процессы освобождения динамической памяти, используемой объектами , которые больше не используются приложением. В таких языках, как или C или C ++, разработчик часто отвечает за управление динамической памятью (используя malloc и free или new и delete ). Однако в Java эта задача оставлена ​​за так называемым сборщиком мусора. Сборщик мусора автоматически освобождает неиспользуемую память, освобождая разработчика от значительной части этого неблагодарного жонглирования памяти.

Самый основной алгоритм сборки мусора работает, начиная с корневых объектов (т. Е. Объектов в стеке потоков, статических объектов и т. Д.), Которые являются живыми (в реальном времени означает использование в настоящее время), и затем перебирая все доступные объекты. Любой объект, который не может быть достигнут таким способом, является мусором и может быть собран. Приложение приостановлено, пока этот процесс продолжается. Это называется пометкой и разверткой — сначала вы помечаете объекты, которые являются живыми, затем вы сканируете объекты, которых нет. Время, необходимое для этого, очевидно, пропорционально количеству живых объектов (которое может быть довольно большим числом в современных приложениях Java), и поэтому были разработаны более эффективные схемы сбора.

ГЦ-пространство

Одна из таких схем проистекает из естественного факта, что вы можете разделять объекты в зависимости от того, сколько они живут Большинство приложений создают много объектов с очень коротким сроком службы и меньше объектов, которые находятся вокруг в течение длительного времени (я видел оценки, что для среднего приложения 85-98% выделенных объектов являются недолговечными). Вы можете воспользоваться этим фактом, делая коллекции. В Java объекты выделяются из области памяти, известной как куча, Куча Java обычно делится на несколько пробелов (обычно она одинакова для разных реализаций, но есть странное исключение или два). Основными областями являются молодое поколение, штатное поколение (также называемое старым поколением) и постоянное поколение. Молодое поколение затем подразделяется на райское пространство и два пространства выживших. Постоянное поколение обычно предназначено для объектов, которые существуют на протяжении всего жизненного цикла приложения (внутренние строки, объекты классов и т. Д.) И обычно не играют большой роли в сборе мусора. Размер постоянного поколения не является частью области кучи, определенной с помощью -Xms и -Xmx. Хотя это очень необычная потребность, все же стоит отметить, что при необходимости можно собрать постоянное поколение, используя:

-XX:+CMSPermGenSweepingEnabled

Когда объекты создаются впервые, они размещаются в пространстве eden. Когда пространство eden становится полным, все еще живые объекты внутри него копируются в одно из оставшихся в живых пространств (или, если они не помещаются, в постоянное пространство). Одно пространство выживших всегда остается пустым, и в каждой коллекции молодого поколения (второстепенной коллекции) живые объекты из пространства Эдема и непустого пространства выживших копируются в пустое пространство выживших. Это оставляет недавно освобожденное пространство выживших для следующего раунда, так как все еще живые объекты в ранее полном пространстве выживших будут скопированы в оставшееся пространство.

Как вы можете видеть, вместо того, чтобы теперь бегать по каждому объекту для каждой коллекции, вы можете собирать молодые поколения чаще, а постоянное поколение (долгоживущие объекты) гораздо реже. Вы также можете оптимизировать свою коллекцию под характеристики пространства — то есть обычно почти все объекты в молодом пространстве будут мусором. В общем случае объект должен пережить пару небольших коллекций, чтобы попасть в арендованное пространство (сначала превратив его в пространство выживших, а затем в арендованное пространство). Копирующий сборщик идентифицирует мусор путем копирования живых объектов из одного пространства в другое — все, что осталось, по определению является мусором. Sun JDK использует копирующие коллекторы для молодого помещения и коллекторы марки и развертки для арендованного пространства.

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

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

Объем оперативной памяти, доступной для различных пространств, зависит от размера кучи, выделенной JVM. Значения по умолчанию выбираются в зависимости от обнаруженного оборудования, но обычно вы можете добиться большего, указав хороший Xms, Xmx самостоятельно. На сервере может быть хорошей идеей связать эти две настройки вместе, чтобы JVM не теряла время на изменение размера. Как правило, вы не хотите, чтобы размер кучи был намного больше необходимого — это может без необходимости увеличить стоимость полных сборок мусора и отнять ОЗУ от других важных операций, таких как кэширование файловой системы.

-Xms
Начальный размер кучи
-Xmx
Максимальный размер кучи

Замечание об опциях JVM Cmd Line

  • Логические параметры —   On : -XX: + <option> Off : -XX: — <option>.
  • Числовые параметры: -XX: <опция> = <число>. Числа могут включать в себя «m» или «M» для мегабайт, «k» или «K» для килобайт, и «g» или «G» для гигабайт (1M = 1048576). В случае Xms и Xmx используется только один X без двоеточия.
  • Опции строки: -XX: <опция> = <строка>

Определение размеров отдельных пространств

Обычно вы хотите выделить много памяти молодому поколению, особенно если у вас есть несколько процессоров, поскольку распределение может быть распараллелено, и каждый поток получит свой собственный фрагмент пространства eden для работы. Вы, как правило, хотите, чтобы у молодого поколения было меньше половины пространства заемного поколения, особенно при использовании Сериализованного коллектора. Около 33% — это, как правило, хорошее число для начала. Лучший размер будет варьироваться от приложения к приложению в зависимости от его распределения молодых и долгоживущих объектов. Вы не хотите, чтобы молодое пространство было настолько маленьким, что многие недолговечные объекты складываются в постоянное пространство. Вы также обычно не хотите, чтобы оно было настолько большим, чтобы в арендуемом пространстве не было достаточно свободного места, и / или коллекции молодого поколения начали занимать слишком много времени.

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

-XX:NewSize
(Начиная с 5.0) Размер молодого поколения при запуске JVM — рассчитывается автоматически при указании NewRatio
-XX:MaxNewSize
(Начиная с версии 1.4) Максимальный размер, до которого может вырасти молодое поколение (неограниченно, если не указано)
-Xmn
Устанавливает для нового поколения фиксированный размер — обычно это не рекомендуется, если вы не исправляете и другие размеры памяти.
-XX:NewRatio
Устанавливает размер нового поколения как отношение к постоянному размеру поколения.
-XX:SurvivorRatio
Вы также можете контролировать размер пространств для выживших — на практике это обычно не очень полезно.

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

Есть несколько полезных инструментов, которые помогут вам понять процесс сбора мусора.

Знакомство с сборкой мусора

Вы можете использовать следующие параметры командной строки для генерации информации о процессе сборки мусора:

-verbose:gc
Распечатайте информацию о куче и gc на каждой коллекции.
-XX:+PrintGCDetails
(Начиная с версии 1.4) Печать дополнительной информации о сборке мусора.
-XX:+PrintGCTimeStamps
(Начиная с версии 1.4) Добавьте метки времени в журналы сбора мусора.
-Xloggc:C:\whereever\gc.log
Укажите файл журнала.

Существуют различные инструменты, которые помогут вам расшифровать эти журналы. Одним из них является GCViewer — хотя он знает только, как читать журналы gc до Java 5.0 (хотя он может частично читать файлы 6.0). Еще одна хорошая опция от IBM — PMAT , и она может читать журналы Java 6 gc.

Существует также очень крутой инструмент под названием VisualGC, который вы можете использовать для визуального наблюдения за тем, как объекты перемещаются между пространствами в реальном времени во время работы вашего приложения. Это доступно как отдельное приложение или как плагин для Netbeans и VisualVM .

 

 

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

Следующее относится к реализации Sun Java, а также к OpenJDK.

Существует три основных схемы сборки мусора, которые вам следует рассмотреть (большая часть относится к Java 1.4, но в целом я нацеливаюсь на Java 1.5 и выше). Эти схемы часто называют самими сборщиками, но обычно каждая включает в себя два сборщика — один для старого пространства и один для нового пространства. Эти схемы коллектора часто называются их старыми именами космического коллектора: Сериализированный коллектор , Пропускной коллектор и Параллельный коллектор с низкой паузой .

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

 

Сериализованный коллектор

Cmd Line Arg
-XX:+UseSerialGC (Since 5.0)
New Space Collector Серийный — однопоточный, останови мир, копирующий коллектор
Старый космический коллекционер Serial Old — однопоточный, останови мир, mark-sweep-compact коллектор

С сериализованным коллектором основная коллекция выполняется, когда заполненное пространство сохраняется. Это известно как коллекция «останови мир», потому что все потоки приложения будут приостановлены во время сбора.

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

Этот сборщик — единственный, который я видел в отношении -XX: MaxHeapFreeRatio — хотя это все еще происходит, только если запущена полная коллекция. Если вы пытаетесь свести к минимуму использование оперативной памяти и всегда возвращать операционке как можно больше памяти, используйте сериализованный сборщик и агрессивный ключ -XX: MaxHeapFreeRatio может быть хорошей стратегией. Возможно, вы захотите иногда вызывать полную коллекцию с помощью System.gc (), когда ваше приложение бездействует.

 

Пропускной коллектор (также известный как параллельный коллектор)

Cmd Line Arg
-XX:+UseParallelGC (Since 1.4.1)
New Space Collector Parallel Scavenge — многопоточный, остановить мир, копировать коллектор
Старый космический коллекционер Serial Old — однопоточный, останови мир, mark-sweep-compact коллектор

Коллектор пропускной способности использует параллельную версию коллектора молодого поколения, в то время как постоянное поколение все еще будет использовать последовательный коллектор. Таким образом, в то время как один поток все еще будет выполнять сборы в постоянном пространстве, несколько потоков будут работать вместе, собирая молодые пространства.

В Java 1.5 update 6 была добавлена ​​функция, называемая параллельным уплотнением — эта функция позволяет сборщику пропускной способности параллельно выполнять основные коллекции. Вы можете включить это с -XX: + UseParallelOldGC . Использование этого должно очень помочь с масштабируемостью, так как вы обходите узкое место потока с одной коллекцией на очень больших кучах (несколько гигабайт). Я читал, что это может на самом деле снизить производительность на меньших кучах из-за конфликта блокировки.

Сборщик пропускной способности должен быть сборщиком по умолчанию, выбранным на компьютерах серверного класса (в Java 1.5 и выше), но есть исключения — например, мой MacbookPro по умолчанию использует сборщик CMS. Вы всегда можете переопределить эти значения по умолчанию.

Пропускная способность обычно наиболее полезна, когда в вашем приложении имеется большое количество потоков, создающих новые объекты, и у вас доступно более одного процессора (хотя лучше использовать более двух). Как правило, когда у вас есть несколько потоков, выделяющих объекты, вы также хотите увеличить размер молодого поколения. Количество потоков сборщика мусора, как правило, будет равно количеству процессоров, которое у вас есть, но вы можете управлять этим числом с помощью -XX: ParallelGCThreads = n. Иногда вам может понадобиться уменьшить количество потоков, поскольку каждый из них зарезервирует часть поколения с постоянным доступом для рекламных акций — это может вызвать эффект фрагментации и эффективно снизить размер поколения с постоянным доступом (обычно это проблема, только если ваше приложение имеет доступ ко многим процессорам или ядрам).

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

-XX: MaxGCPauseMillis = n подсказка сборщику пропускной способности, что требуется максимальное время паузы n миллисекунд. По умолчанию подсказки нет. Сборщик отрегулирует размер кучи и другие параметры сбора в попытке удовлетворить подсказку — имейте в виду, что пропускная способность может быть принесена в жертву при попытке достичь этой цели. Также нет гарантии, что цель будет достигнута.

Вы также можете указать целевую цель о том, сколько времени затрачивается на сборку мусора по сравнению с запуском вашего приложения с использованием -XX: GCTimeRatio . По умолчанию это значение равно 1% (имейте в виду, что эти значения по умолчанию, как правило, меняются от выпуска к выпуску).

С помощью сериализованного сборщика мусора генерация собирается, когда она заполнена (т. Е. Когда дальнейшее распределение не может быть выполнено из этого поколения). Это также верно для пропускной способности коллектора.

 

Параллельный коллектор с низкой паузой

Cmd Line Arg
-XX:+UseConcMarkSweepGC (Since 1.4.1)
New Space Collector Par New — многопоточный, Stop the World, копирующий коллектор, работающий с CMS
Старый космический коллекционер Обычно CMS , в основном параллельный коллектор с низкой паузой — если только не происходит сбой параллельного режима, в этом случае Serial Old — однопоточный, остановите мир, mark-sweep-compact коллектор

Используйте параллельный сборщик с низкой паузой, когда вы можете позволить себе использовать ресурсы процессора совместно с сборщиком мусора во время работы приложения. Обычно это хорошо для приложений с большим количеством долгоживущих данных — это означает, что вам нужно большое пространство для генерации. Очевидно, что наличие нескольких процессоров также полезно. Этот сборщик по-прежнему приостанавливает потоки приложения в коллекции дважды — один раз на короткое время в начале (когда он отмечает объекты, непосредственно доступные из корневых объектов), и немного более длинную паузу к середине (когда он выполняет поиск, чтобы найти то, что пропустил из-за параллельной маркировки ) — остальная часть коллекции выполняется одновременно с использованием одного из доступных процессоров (или одного потока). Если этот сборщик не может завершить сбор арендованного пространства до его заполнения,все потоки будут приостановлены, и будет выполнен полный сбор данных — это называется отказом параллельного режима и, вероятно, означает, что вам необходимо настроить параметры одновременного сбора.

Этот сборщик используется для генерации с постоянным доступом и выполняет сбор одновременно с выполнением приложения. Этот сборщик также может быть связан с параллельной версией сборщика молодого поколения ( -XX: + UseParNewGC ).

Обратите внимание, что — XX: + UseParallelGC (сборщик пропускной способности) не следует использовать с  -XX: + UseConcMarkSweepGC , и JVM завершится ошибкой при запуске, если вы попробуете это с большинством современных JVM. То же самое с -XX: + UseParallelOldGC .

Параллельный коллектор с малой паузой будет вести статистику, чтобы он мог лучше угадать, когда начинать сбор (чтобы он завершился до того, как заполненное пространство зарезервировано) — также, однако, он начнет собирать, когда зарезервированное пространство достигнет процента от того, что доступно — вы можно вручную установить это с -XX: CMSInitiatingOccupancyFraction = n. Значение по умолчанию для этого параметра зависит от JVM. Я читал, что по умолчанию для 1,5 было 68%, в то время как по умолчанию для 1,6 составляет 92%. Вы можете уменьшить его, если необходимо, чтобы сбор был запущен раньше, и тогда вы с большей вероятностью завершите сбор до того, как заполненное пространство будет заполнено.

The concurrent low pause collector can also be used in an incremental mode that I will not go into here. This mode causes the low pause collector to occasional yield the processor used for parallel collection back to the application, and thereby lessen its impact on application performance.

 

The Parallel Young Generation Collector

-XX:+UseParNewGC

This collector is much like the throughput collector in that it collects the young generation in parallel. Most of what applies to the throughput collector also applies to this collector, however a different implementation is used that allows this collector to work in conjunction with the concurrent low pause collector, unlike the throughput collector. Despite some Sun/Oracle literature indicating this is off by default, it does seem to be on by default when using CMS in at least Java 6. You can disable it with:

-XX:+UseConcMarkSweepGC -XX:-UseParNewGC

The flip side of that coin is that while the throughput garbage collector (-XX:+UseParallelGC) can be used with adaptive sizing (-XX:+UseAdaptiveSizePolicy), the parallel young generation collector (-XX:+UseParNewGC) cannot.

-XX:+UseAdaptiveSizePolicy records statistics about GC times, allocation rates, and free space, and then sizes the young and tenured generations to best fit those statistics. This is for use with the throughput collector and is on by default.

 

Choosing a Collector

Note: this article is biased towards server applications and the -server hotspot vm.

Usually you just want to start with the Parallel (throughput) collector. It’s the one that has ergonomics, and it will automatically adjust key settings so that most server apps will do just fine. This is the default collector on most server class systems. In general, you do not need to change any garbage collection settings until you have determined you have a garbage collection issue to solve.

When you have to confront very large heaps, the Parallel collector can start to break down – it collects the tenured space using a stop the world collection, meaning your app is frozen while the collections happens. So when you find that the Parallel collector is just not cutting it, even when using UseParallelOldGC, you might try the mostly Concurrent Low-Pause Collector. It will collect as your application is running using a thread on the side, with two much shorter stop-the-world pauses. Overall, the CMS collector is slower in terms of throughput – but your application will likely be frozen less often.

Ergonomics do not apply here, so you are on your own for coming up with good settings if the defaults don’t turn out to be a good fit – but you can often remove long “the world is stopped” pauses with this collector.

The hope is that it is just going to make sense to always use the G1 collector in the future – it attempts to offer the best of both worlds of the throughput and mostly concurrent low pause collectors.

The Garbage First (G1) Collector

The Garbage First Collector is a new garbage collector that intends to rule them all. It is available in Sun Java 6 update 14 as well as recent versions of OpenJDK6 and early versions of OpenJDK 7. Eventually I plan to write more about his collector. Briefly: the G1 collector should combine the best of both the throughput and mostly concurrent low pause collectors. It uses new strategies to minimize stop the world pauses and maintain high throughput on multiprocessor systems with very large heaps.

Try this collector with:

-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC