Механизм сбора мусора платформы Java значительно повысил эффективность разработки, но плохо реализованный сборщик мусора может потреблять слишком много ресурсов приложения. В третьей части серии статей по оптимизации производительности виртуальных машин Java Ева Андреассон знакомит новичков в Java с моделью памяти и механизмом сборки мусора платформы Java. Она объясняет, почему фрагментация (а не сборка мусора) является основной проблемой производительности приложений Java и почему сборка и сжатие мусора на уровне поколений в настоящее время являются основными (но не самыми инновационными) способами борьбы с фрагментацией приложений Java.
Целью сборки мусора (GC) является освобождение памяти, занятой объектами Java, на которые больше не ссылаются никакие активные объекты. Это основная часть механизма управления динамической памятью виртуальной машины Java. Во время типичного цикла сборки мусора все объекты, на которые еще есть ссылки (и, следовательно, доступны), сохраняются, а те, на которые больше нет ссылок, освобождаются, а занимаемое ими пространство освобождается для выделения новым объектам.
Чтобы понять механизм сборки мусора и различные алгоритмы сборки мусора, сначала необходимо кое-что узнать о модели памяти платформы Java.
Сборка мусора и модель памяти платформы Java
Когда вы запускаете Java-программу из командной строки и указываете параметр запуска -Xmx (например: java -Xmx:2g MyApp), Java-процессу выделяется память указанного размера, которая представляет собой так называемую Java-кучу. . Это выделенное адресное пространство памяти используется для хранения объектов, созданных программами Java (а иногда и JVM). По мере того как приложение работает и постоянно выделяет память для новых объектов, куча Java (то есть выделенное адресное пространство памяти) будет медленно заполняться.
В конце концов куча Java заполнится, а это означает, что поток выделения памяти не сможет найти достаточно большое непрерывное пространство для выделения памяти для нового объекта. В этот момент JVM решает уведомить сборщик мусора и начать сбор мусора. Сбор мусора также можно запустить, вызвав System.gc() в программе, но использование System.gc() не гарантирует, что сбор мусора будет выполнен. Перед любой сборкой мусора механизм сборки мусора сначала определяет, безопасно ли выполнять сборку мусора. Когда все активные потоки приложения находятся в безопасной точке, можно запустить сборку мусора. Например, сбор мусора не может быть выполнен, когда память выделяется для объекта, или сбор мусора не может быть выполнен во время оптимизации инструкций ЦП, поскольку существует вероятность потери контекста и конечный результат будет неверным.
Сборщик мусора не может вернуть объект с активными ссылками, что нарушит спецификацию виртуальной машины Java. Нет необходимости немедленно перерабатывать мертвые объекты, поскольку мертвые объекты в конечном итоге будут переработаны при последующей сборке мусора. Хотя существует множество способов реализации сборки мусора, два вышеуказанных пункта одинаковы для всех реализаций сборки мусора. Настоящая проблема сборки мусора заключается в том, как определить, жив ли объект, и как освободить память, не затрагивая при этом приложение, насколько это возможно. Таким образом, сборщик мусора преследует следующие две цели:
1. Быстро освободите неиспользуемую память, чтобы удовлетворить потребности приложения в выделении памяти и избежать ее переполнения.
2. Минимизируйте влияние на производительность работающих приложений (задержку и пропускную способность) при освобождении памяти.
Два типа сбора мусора
В первой статье этой серии я представил два метода сборки мусора, а именно подсчет ссылок и отслеживание сбора. Далее мы более подробно рассмотрим эти два подхода и представим некоторые алгоритмы сбора трассировки, используемые в производственных средах.
Коллектор подсчета ссылок
Сборщик подсчета ссылок записывает количество ссылок, указывающих на каждый объект Java. Как только количество ссылок, указывающих на объект, достигает 0, объект можно немедленно переработать. Эта оперативность является основным преимуществом сборщика с подсчетом ссылок, и почти нет накладных расходов на поддержание памяти, на которую не указывают ссылки, но отслеживание последнего счетчика ссылок для каждого объекта обходится дорого.
Основная трудность коллектора подсчета ссылок заключается в том, как обеспечить точность подсчета ссылок. Другая известная трудность — как бороться с циклическими ссылками. Если два объекта ссылаются друг на друга и на них не ссылаются другие живые объекты, то память этих двух объектов никогда не будет освобождена, поскольку количество ссылок, указывающих ни на один из объектов, равно 0. Переработка в памяти циклических ссылочных структур требует серьезного анализа (Примечание переводчика: Глобальный анализ в куче Java), что увеличивает сложность алгоритма и, таким образом, приводит к дополнительным накладным расходам для приложения.
сборщик следов
Сборщик трассировки основан на предположении, что все живые объекты можно найти путем итерации ссылок (ссылок и ссылок на ссылки) на известный исходный набор живых объектов. Начальный набор активных объектов (также называемых корневыми объектами) можно определить путем анализа регистров, глобальных объектов и кадров стека. После определения исходного набора объектов отслеживающий сборщик следит за ссылочными отношениями этих объектов и последовательно помечает объекты, на которые указывают ссылки, как активные объекты, так что набор известных активных объектов продолжает расширяться. Этот процесс продолжается до тех пор, пока все объекты, на которые имеются ссылки, не будут помечены как живые объекты, а память тех объектов, которые не были отмечены, не будет освобождена.
Сборщик отслеживания отличается от коллектора подсчета ссылок главным образом тем, что он может обрабатывать циклические структуры ссылок. Большинство сборщиков трассировки обнаруживают объекты, на которые нет ссылок, в циклических ссылочных структурах на этапе маркировки.
Сборщик трассировки — наиболее часто используемый метод управления памятью в динамических языках и в настоящее время является наиболее распространенным методом в Java. Он также проверен в производственных средах в течение многих лет. Ниже я представлю сборщик трассировки, начиная с некоторых алгоритмов реализации сбора трассировки.
Алгоритм сбора трассировки
Копирование сборщиков мусора и сборщиков мусора с маркировкой не являются чем-то новым, но они по-прежнему остаются двумя наиболее распространенными алгоритмами для реализации отслеживаемых сборок мусора на сегодняшний день.
Копирование сборщика мусора
Традиционный копирующий сборщик мусора использует два адресных пространства в куче (т. е. из пространства и в пространство). При выполнении сбора мусора активные объекты из пространства «из» копируются в пространство «в». удаляются (Примечание переводчика: после копирования в пространство или в старое поколение), все пространство из пространства может быть переработано. При повторном выделении пространства сначала будет использоваться пространство to (Примечание переводчика: то есть пространство to). предыдущего раунда будет использоваться как новый раунд из космоса).
В ранней реализации этого алгоритма поля from и to постоянно меняли свои позиции. То есть, когда пространство to заполнено и запускается сбор мусора, пространство to становится пространством from, как показано на рисунке 1. .
Рис. 1. Традиционная последовательность сборки мусора при копировании
Новейший алгоритм копирования позволяет использовать любое адресное пространство в куче как в пространстве, так и из пространства. Таким образом, им не нужно менять позиции друг с другом, а просто логически менять позиции.
Преимущество копирующего коллектора в том, что копируемые объекты в пространстве располагаются компактно и фрагментация отсутствует вообще. Фрагментация — распространенная проблема, с которой сталкиваются другие сборщики мусора, и это также основная проблема, о которой я расскажу позже.
Недостатки сборщика копий
Вообще говоря, копирующий сборщик останавливает мир, а это означает, что пока идет сбор мусора, приложение не может выполняться. При такой реализации чем больше данных вам нужно скопировать, тем сильнее это повлияет на производительность приложения. Это недостаток для приложений, чувствительных ко времени отклика. При использовании сборщика копий также необходимо учитывать худший сценарий (то есть все объекты в пространстве являются активными объектами). На данный момент вам необходимо подготовить достаточно большое пространство для перемещения этих активных объектов, чтобы можно было их перемещать. Пространство должно быть достаточно большим для установки всех объектов из космоса. Из-за этого ограничения использование памяти алгоритмом копирования немного недостаточно (Примечание переводчика: в худшем случае пространство to должно быть того же размера, что и пространство from, поэтому использование только 50%).
коллектор очистки отметок
Большинство коммерческих JVM, развернутых в производственных средах предприятий, используют сборщик пометок (или пометок), поскольку он не повторяет влияние сборщика мусора на производительность приложения. К самым известным сборщикам марок относятся CMS, G1, GenPar и DeterministicGC.
Коллектор пометки-развертки отслеживает ссылки на объекты и помечает каждый найденный объект как живой, используя бит флага. Этот флаг обычно соответствует адресу или группе адресов в куче. Например: активный бит может быть битом в заголовке объекта (Примечание переводчика: бит), битовым вектором или растровым изображением.
После завершения маркировки наступает этап очистки. На этапе очистки обычно снова проходит куча (не только объекты, помеченные как активные, но и вся куча), чтобы найти немаркированные смежные адресные пространства памяти (немаркированная память свободна и подлежит переработке), а затем сборщик организует их в свободные списки. Сборщик мусора может иметь несколько списков свободных (обычно разделенных в зависимости от размера блока памяти). Некоторые сборщики JVM (например: JRockit Real Time) даже динамически делят список свободных на основе анализа производительности приложения и статистики размера объектов.
После этапа очистки приложение может снова выделить память. При выделении памяти для нового объекта из списка свободных вновь выделенный блок памяти должен соответствовать размеру нового объекта, среднему размеру объекта потока или размеру TLAB приложения. Поиск блоков памяти подходящего размера для новых объектов помогает оптимизировать память и уменьшить фрагментацию.
Марк - Устранение дефектов коллекционера
Время выполнения фазы маркировки зависит от количества активных объектов в куче, а время выполнения фазы очистки зависит от размера кучи. Поэтому в ситуациях, когда настройка кучи велика и в куче много активных объектов, алгоритм разметки-развертки будет иметь определенное время паузы.
Для приложений, интенсивно использующих память, вы можете настроить параметры сборки мусора в соответствии с различными сценариями и потребностями приложения. Во многих случаях эта корректировка, по крайней мере, откладывает риск, связанный с этапом маркировки/проверки SLA приложения или соглашения об обслуживании (здесь SLA относится к времени ответа, которого должно достичь приложение). Но настройка эффективна только для определенных нагрузок, а изменения нагрузки или модификации самого приложения требуют повторной настройки.
Реализация коллектора развертки по меткам
Существует как минимум два коммерчески проверенных метода реализации сборки мусора с очисткой по меткам. Один из них — параллельная сборка мусора, а другой — параллельная (или большую часть времени параллельная) сборка мусора.
Параллельный коллектор
Параллельная сборка означает, что ресурсы используются параллельно потоками сборки мусора. Большинство коммерческих реализаций параллельного сбора представляют собой сборщики мусора с остановкой, в которых все потоки приложений приостанавливаются до завершения сборки мусора. Поскольку сборщики мусора могут эффективно использовать ресурсы, они обычно показывают лучшие результаты в тестах пропускной способности, например. СПЕЦjbb. Если пропускная способность имеет решающее значение для вашего приложения, параллельный сборщик мусора — хороший выбор.
Основная цена параллельного сбора (особенно для производственных сред) заключается в том, что потоки приложений не могут правильно функционировать во время сбора мусора, как и копирующий сборщик. Поэтому использование параллельных сборщиков окажет существенное влияние на приложения, чувствительные ко времени отклика. Особенно когда в куче много сложных структур активных объектов, существует множество ссылок на объекты, которые необходимо отслеживать. (Помните, что время, необходимое сборщику пометок и очистки для освобождения памяти, зависит от времени, необходимого для отслеживания коллекции живых объектов, плюс времени, необходимого для обхода всей кучи.) При параллельном подходе приложение приостанавливается на некоторое время. все время вывоза мусора.
параллельный сборщик
Параллельные сборщики мусора больше подходят для приложений, чувствительных ко времени отклика. Параллелизм означает, что поток сборки мусора и поток приложения выполняются одновременно. Поток сборки мусора не владеет всеми ресурсами, поэтому ему необходимо решить, когда начинать сборку мусора, давая достаточно времени для отслеживания активной сборки объектов и освобождения памяти до того, как память приложения переполнится. Если сборка мусора не будет завершена вовремя, приложение выдаст ошибку переполнения памяти. С другой стороны, вы не хотите, чтобы сборка мусора занимала слишком много времени, поскольку она будет потреблять ресурсы приложения и влиять на пропускную способность. Поддержание этого баланса требует навыков, поэтому эвристика используется для определения того, когда начинать сбор мусора, а когда выбирать оптимизацию сборки мусора.
Другая трудность заключается в определении того, когда безопасно выполнять некоторые операции (операции, требующие полного и точного снимка кучи), например, необходимость знать, когда фаза маркировки завершена, чтобы можно было перейти к фазе очистки. Это не проблема для параллельного сборщика, останавливающего мир, поскольку мир уже приостановлен (Примечание переводчика: поток приложения приостановлен, а поток сбора мусора монополизирует ресурсы). Но для одновременных сборщиков может быть небезопасно немедленно переключаться с фазы маркировки на фазу очистки. Если поток приложения изменяет часть памяти, которая отслеживается и помечается сборщиком мусора, могут создаваться новые немаркированные ссылки. В некоторых реализациях параллельного сбора это может привести к тому, что приложение застрянет в цикле повторяющихся аннотаций на долгое время, не имея возможности получить свободную память, когда приложению эта память понадобится.
Из обсуждения мы знаем, что существует множество сборщиков мусора и алгоритмов сборки мусора, каждый из которых подходит для определенных типов приложений и различных нагрузок. Не просто разные алгоритмы, а разные реализации алгоритмов. Поэтому лучше всего понять потребности приложения и его собственные характеристики, прежде чем указывать сборщик мусора. Далее мы представим некоторые подводные камни модели памяти платформы Java. Под ловушками здесь подразумеваются некоторые предположения, которые Java-программисты склонны делать в динамически изменяющейся производственной среде, которые ухудшают производительность приложений.
Почему настройка не может заменить сборку мусора
Большинство Java-программистов знают, что существует множество вариантов оптимизации Java-программ. Несколько дополнительных параметров JVM, сборщика мусора и настройки производительности позволяют разработчикам тратить много времени на бесконечную настройку производительности. Это привело некоторых людей к выводу, что сбор мусора — это плохо, и что настройка, позволяющая сделать сбор мусора менее частым или более коротким, является хорошим обходным решением, но это рискованно.
Рассмотрите возможность настройки для конкретного приложения. Большинство параметров настройки (таких как скорость выделения памяти, размер объекта, время отклика) основаны на скорости выделения памяти приложения (Примечание переводчика: или другие параметры) на основе текущего объема тестовых данных. В конечном итоге это может привести к следующим двум результатам:
1. Вариант использования, прошедший тестирование, терпит неудачу в производстве.
2. Изменения объема данных или изменения в приложениях требуют перенастройки.
Настройка является итеративной, и, в частности, параллельные сборщики мусора могут потребовать значительной настройки (особенно в производственной среде). Эвристика необходима для удовлетворения потребностей приложения. Чтобы соответствовать наихудшему сценарию, результатом настройки может стать очень жесткая конфигурация, что также приводит к большой трате ресурсов. Этот подход к настройке — донкихотский квест. Фактически, чем больше вы оптимизируете сборщик мусора для соответствия конкретной нагрузке, тем дальше вы от динамической природы среды выполнения Java. В конце концов, сколько приложений имеют стабильную загрузку и насколько надежной можно ожидать эту загрузку?
Итак, если вы не занимаетесь настройкой, что вы можете сделать, чтобы предотвратить ошибки нехватки памяти и улучшить время отклика? Первым делом необходимо найти основные факторы, влияющие на производительность Java-приложений.
фрагментация
Фактором, влияющим на производительность приложения Java, является не сборщик мусора, а фрагментация и то, как сборщик мусора обрабатывает фрагментацию. Так называемая фрагментация — это состояние, когда в куче есть свободное пространство, но недостаточно большого непрерывного пространства памяти для выделения памяти для новых объектов. Как упоминалось в первой статье, фрагментация памяти — это либо TLAB пространства, оставшегося в куче, либо пространство, занимаемое небольшими объектами, которые высвобождаются среди долгоживущих объектов.
Со временем и по мере работы приложения эта фрагментация распространяется по всей куче. В некоторых случаях использование статически настроенных параметров может оказаться хуже, поскольку они не отвечают динамическим потребностям приложения. Приложения не могут эффективно использовать это фрагментированное пространство. Если ничего не сделать, это приведет к последовательным сборкам мусора, при которых сборщик мусора попытается освободить память для выделения новым объектам. В худшем случае даже последовательные сборки мусора не могут освободить больше памяти (слишком большая фрагментация), и тогда JVM приходится выдавать ошибку переполнения памяти. Вы можете устранить фрагментацию, перезапустив приложение, чтобы в куче Java было непрерывное пространство памяти для размещения новых объектов. Перезапуск программы приводит к простою, и через некоторое время куча Java снова наполняется фрагментами, что приводит к повторному перезапуску.
Ошибки нехватки памяти, приводящие к зависанию процесса, и журналы, показывающие, что сборщик мусора перегружен, указывают на то, что сборщик мусора пытается освободить память и что куча сильно фрагментирована. Некоторые программисты попытаются решить проблему фрагментации путем повторной оптимизации сборщика мусора. Но я думаю, что нам следует найти более инновационные способы решения этой проблемы. Следующие разделы будут посвящены двум решениям проблемы фрагментации: сборке мусора поколений и уплотнению.
Сбор мусора по поколениям
Возможно, вы слышали теорию о том, что большинство объектов в производственной среде недолговечны. Сборка мусора поколений — это стратегия сбора мусора, основанная на этой теории. При поколенческой сборке мусора мы делим кучу на разные пространства (или поколения), и в каждом пространстве хранятся объекты разного возраста. Так называемый возраст объекта — это количество циклов сборки мусора, которые объект пережил (т. е.). сколько лет объекту), на который все еще ссылаются после циклов сборки мусора).
Когда в новом поколении не осталось места для выделения, активные объекты нового поколения будут перемещены в старое поколение (обычно существует только два поколения). Примечание переводчика: в новое поколение будут перемещены только объекты, соответствующие определенному возрасту. Старое поколение). Сборка мусора часто использует односторонний сборщик копий. Некоторые современные JVM используют параллельные сборщики мусора в новом поколении. Конечно, для нового поколения и старого поколения могут быть реализованы разные алгоритмы сбора мусора. Если вы используете параллельный сборщик или копирующий сборщик, ваш юный коллекционер — это сборщик, останавливающий мир (см. предыдущее объяснение).
Старое поколение выделяется объектам, которые были перемещены из нового поколения. На эти объекты либо ссылаются в течение длительного времени, либо на них ссылается некоторая коллекция объектов нового поколения. Иногда крупные объекты передаются непосредственно старому поколению, поскольку стоимость перемещения крупных объектов относительно высока.
Поколенческая технология сбора мусора
При поколенческой сборке мусора сбор мусора выполняется реже в старом поколении и чаще в новом, и мы также надеемся, что цикл сборки мусора в новом поколении будет короче. В редких случаях молодое поколение может собираться чаще, чем старое поколение. Это может произойти, если вы сделаете молодое поколение слишком большим и большинство объектов в вашем приложении будут жить долго. В этом случае, если старое поколение установлено слишком маленьким для размещения всех долгоживущих объектов, сборщику мусора старого поколения также будет сложно освободить место для перемещаемых объектов. Однако, вообще говоря, сборка мусора по поколениям может позволить приложениям достичь более высокой производительности.
Еще одним преимуществом разделения нового поколения является то, что оно в некоторой степени решает проблему фрагментации или откладывает худший сценарий. Эти небольшие объекты с коротким временем существования могли вызвать проблемы с фрагментацией, но все они удаляются с помощью сборки мусора нового поколения. Поскольку долгоживущим объектам при перемещении в старое поколение выделяется более компактное пространство, старое поколение также более компактно. Со временем (если ваше приложение работает достаточно долго) старое поколение также станет фрагментированным, что потребует выполнения одной или нескольких полных сборок мусора, а JVM также может выдавать ошибки нехватки памяти. Но создание нового поколения откладывает худший сценарий, которого достаточно для многих приложений. Для большинства приложений это снижает частоту остановки сборки мусора и вероятность ошибок нехватки памяти.
Оптимизировать сбор мусора поколений
Как упоминалось ранее, использование сборки мусора по поколениям требует повторной настройки, такой как корректировка размера молодого поколения, скорости продвижения по службе и т. д. Я не могу подчеркнуть компромисс для конкретной среды выполнения приложения: выбор фиксированного размера оптимизирует приложение, но также снижает способность сборщика мусора справляться с динамическими изменениями, которые неизбежны.
Первый принцип нового поколения — максимально увеличить его, обеспечив при этом время задержки при сборке мусора, останавливающего мир, и в то же время зарезервировать достаточно места в куче для долговременно сохраняющихся объектов. Вот некоторые дополнительные факторы, которые следует учитывать при настройке сборщика мусора поколений:
1. Большая часть нового поколения — это сборщики мусора, останавливающие мир. Чем больше настройка нового поколения, тем дольше соответствующее время паузы. Поэтому для приложений, на которые сильно влияет время паузы при сборке мусора, внимательно учитывайте размер молодого поколения.
2. В разных поколениях могут использоваться разные алгоритмы сборки мусора. Например, в молодом поколении используется параллельная сборка мусора, а в старом поколении — параллельная сборка мусора.
3. Когда обнаруживается, что частое продвижение (Примечание переводчика: переход от нового поколения к старому поколению) не дает результата, это означает, что в старом поколении слишком много фрагментов, а значит, в старом поколении недостаточно места. для хранения объектов, перешедших из нового поколения. На этом этапе вы можете настроить скорость продвижения (т. е. настроить срок продвижения) или убедиться, что алгоритм сборки мусора в старом поколении выполняет сжатие (обсуждается в следующем параграфе), и настроить сжатие в соответствии с нагрузкой приложения. . Также возможно увеличить размер кучи и размер каждого поколения, но это еще больше увеличит время паузы в старом поколении. Знайте, что фрагментация неизбежна.
4. Для таких приложений наиболее подходит поколенческая сборка мусора. У них много мелких объектов с коротким временем существования. Многие объекты перерабатываются на первом этапе цикла сборки мусора. В таких приложениях сбор мусора на уровне поколений может эффективно уменьшить фрагментацию и отсрочить ее воздействие.
сжатие
Хотя сборка мусора на основе поколений задерживает возникновение ошибок фрагментации и нехватки памяти, сжатие является единственным реальным решением проблемы фрагментации. Сжатие — это стратегия сбора мусора, которая освобождает смежные блоки памяти путем перемещения объектов, тем самым освобождая достаточно места для создания новых объектов.
Перемещение объектов и обновление ссылок на объекты — это операции остановки мира, которые требуют определенного потребления (за одним исключением, которое будет обсуждаться в следующей статье этой серии). Чем больше объектов выживет, тем дольше будет пауза, вызванная уплотнением. В ситуациях, когда осталось мало места и сильная фрагментация (обычно из-за того, что программа работает в течение длительного времени), может возникнуть пауза в несколько секунд при уплотнении областей с множеством живых объектов, а при приближении к переполнению памяти - сжатие вся куча может занять даже десятки секунд.
Время паузы для сжатия зависит от объема памяти, которую необходимо переместить, и количества ссылок, которые необходимо обновить. Статистический анализ показывает, что чем больше куча, тем большее количество живых объектов необходимо переместить и обновить ссылки. Время паузы составляет около 1 секунды на каждые 1–2 ГБ перемещенных живых объектов, а для кучи размером 4 ГБ вероятно наличие 25% живых объектов, поэтому периодически будут возникать паузы продолжительностью около 1 секунды.
Стена памяти сжатия и приложений
Стена памяти приложения относится к размеру кучи, который может быть установлен перед паузой, вызванной сборкой мусора (например, сжатием). В зависимости от системы и приложения объем памяти большинства приложений Java варьируется от 4 до 20 ГБ. Вот почему большинство корпоративных приложений развертываются на нескольких небольших JVM, а не на нескольких более крупных JVM. Давайте рассмотрим следующее: сколько современных корпоративных проектов и развертываний Java-приложений определяются ограничениями сжатия JVM. В этом случае, чтобы обойти время паузы при дефрагментации кучи, мы остановились на многоэкземплярном развертывании, управление которым обходилось дороже. Это немного странно, учитывая большие возможности хранения данных современного оборудования и необходимость увеличения памяти для Java-приложений корпоративного класса. Почему для каждого экземпляра установлено всего несколько ГБ памяти. Параллельное сжатие разрушит стену памяти, и это тема моей следующей статьи.
Подвести итог
Эта статья представляет собой вводную статью о сборке мусора, которая поможет вам понять концепции и механизмы сборки мусора и, надеюсь, побудит вас читать дальнейшие статьи по этой теме. Многие из обсуждаемых здесь вещей существуют уже давно, а некоторые новые концепции будут представлены в следующей статье. Например, параллельное сжатие в настоящее время реализовано в Zing JVM компании Azul. Это новая технология сборки мусора, которая даже пытается переопределить модель памяти Java, особенно с учетом того, что память и вычислительная мощность сегодня продолжают улучшаться.
Вот некоторые ключевые моменты, касающиеся сбора мусора, которые я суммировал:
1. Различные алгоритмы и реализации сборки мусора адаптируются к различным потребностям приложений. Следящий сборщик мусора является наиболее часто используемым сборщиком мусора в коммерческих виртуальных машинах Java.
2. Параллельная сборка мусора использует все ресурсы параллельно при выполнении сборки мусора. Обычно это сборщик мусора, останавливающий мир, и поэтому он имеет более высокую пропускную способность, но рабочие потоки приложения должны ждать завершения потока сбора мусора, что оказывает определенное влияние на время ответа приложения.
3. Параллельная сборка мусора. Пока выполняется сборка, рабочий поток приложения все еще работает. Параллельный сборщик мусора должен завершить сбор мусора до того, как приложению понадобится память.
4. Сборка мусора поколений помогает задержать фрагментацию, но не может ее устранить. Сборка мусора по поколениям делит кучу на два пространства: одно для новых объектов, а другое для старых объектов. Сборка мусора по поколениям подходит для приложений со множеством небольших объектов с коротким временем жизни.
5. Сжатие — единственный способ решить проблему фрагментации. Большинство сборщиков мусора выполняют сжатие методом остановки мира. Чем дольше работает программа, тем сложнее ссылки на объекты и тем неравномернее распределяются размеры объектов, что приводит к увеличению времени сжатия. Размер кучи также влияет на время уплотнения, поскольку может оказаться больше живых объектов и ссылок, которые необходимо обновить.
6. Настройка помогает задержать ошибки переполнения памяти. Но результатом чрезмерной настройки является жесткая конфигурация. Прежде чем приступить к настройке методом проб и ошибок, убедитесь, что вы понимаете нагрузку на вашу производственную среду, типы объектов вашего приложения и характеристики ссылок на ваши объекты. Слишком жесткие конфигурации могут быть неспособны выдерживать динамические нагрузки, поэтому обязательно осознавайте последствия при установке нединамических значений.
Следующая статья в этой серии: Углубленное обсуждение алгоритма сборки мусора C4 (Concurrent Continuously Compacting Collector), так что следите за обновлениями!
(Полный текст заканчивается)