Обновить
56
Андрей Лыгин@alygin

Оператор ЭВМ

78
Подписчики
Отправить сообщение
Просадка по какому параметру? Время отклика или пропускная способность?
Уплотнять объекты в рамках очищаемой области памяти можно двумя способами:

1. Выискиваем подходящие «дырки» в областях и рассовываем по ним объекты. Это крайне накладной способ, так как вам придется решать задачу, похожую на задачу раскроя, но на самом деле даже более сложную, раз вы планируете оптимизировать ее, пытаясь оставить как можно больше объектов на своих местах. По затратам процессора это в разы больше того алгоритма, что используется при копировании. Для обновления адресов вам также придется заводить отдельную структуру данных, чтобы хранить соответствие старого адреса и нового, и использовать как минимум один дополнительный проход по всем объектам в конце алгоритма. Так как объекты по большей части достаточно маленькие, размер дополнительной структуры данных под хранение соответствий адресов, может не сильно отличаться от размера самих объектов.

2. Вы можете просто перебирать все объекты подряд и копировать их «влево», впритык к предыдущему живому объекту. При этом остается та же самая проблема с необходимостью ведения отдельной структуры под хранение соответствий адресов, что и в первом варианте. И проблема с доп. проходом для обновления адресов тоже никуда не девается.

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

В случае же копирования у нас используется всего один проход по объектам для решения всех трех задач: поиск живых объектов, уплотнение, обновление ссылок.
Мне кажется, текст по приведенной вами ссылке полностью подтверждает то, что я описал:
Threads which run concurrently do not always run simultaneously (eg. 2 threads on 1 core)… although to the end user they may well appear to – provided the cpu switches between them quickly enough.

Два потока на одном ядре, которое вынуждено между ними переключаться — это и есть конкуренция за ресурсы процессора.

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

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

Я бы не сказал, что ровно наоборот. На самом деле, ситуация просто сложнее: CMS пытается как можно дальше разнести по времени паузы remark (так как она большая) и паузу малой сборки. С паузой initial mark он этого делать, действительно, не пытается. Так, по крайней мере, утверждает официальная документация Oracle: Concurrent Mark Sweep Collector (секция Scheduling Pauses). И да, согласен, чем ближе initial mark к последней малой сборке, тем эффективнее будет последующая большая сборка.

Уже сделали.

Ну, JDK 9 еще не выпущен. А в соответствующей задаче они оставляют себе место для отступления.

Не надо советовать эту опцию.

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

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

«конкурентный» — неверный перевод слова «concurrent»

Готов исправить в статье этот термин, если предложите более подходящий в данном конексте вариант.
Правило большого пальца для приложений, ориентированных на высокую пропускную способность и не чувствительных к паузам, это использование Parallel GC, у которого даже есть второе название — throughput collector. Для среднестатистического приложения при больших объемах данных задержки на очистку у Parallel GC могут быть весьма заметными, но общая пропускная способность наверняка будет выше, чем у CMS или G1. Но, как я уже говорил, каждое приложение в чем-то особенно, и подходить к оптимизации сборки мусора лучше со знанием того, как конкретно у вас устроена работа с данными. Иногда небольшая деталь в реализации программы может заставить полностью изменить подход к ее оптимизации. Об этом подробнее мы поговорим в следующей статье.
Согласен, но только отчасти. Я бы перефразировал на "… это часто свидетельствует..." Конечно, в расточительной и протекающей программе нужно прежде всего сосредоточиться на поиске проблем в коде. Но все-таки бывают ситуации, когда можно испытывать проблемы со сборкой мусора и в добротной, абсолютно герметичной программе, не использующей finalizer'ы. А если речь идет о транзакционном сервисе с большой кучей и жестким SLA, то без подкручивания опций JVM редко обходится. Вот мне хабр как раз подсовывает Историю одного garbage collection'а в качестве похожей публикации с примером такой программы. Или вот пример Solr из комментариев выше. Да и не всегда есть возможность код поправить, мы ведь и сторонними инструментами пользуемся.
Да, в G1 своя специфика работы с т. н. громадными объектами, о ней в статье обязательно расскажу. И если их много и они короткоживущие, то можно получить неприятности.
Обязательно, в следующей статье будут CMS и G1.
Представьте себя на месте сборщика. У вас есть регион памяти, который нужно очистить. После удаления мусора регион оказывается сильно дефрагментированным и если вы хотите это исправить, то у вас есть два варианта: либо уплотнять объекты в рамках этого же региона, либо скопировать их в другой, пока еще пустой регион, располагая один-к-одному, а старый регион объявить пустым. Но задача осложняется тем, что объекты ссылаются друг на друга и при перемещении любого объекта необходимо производить обновление всех имеющихся на него ссылок. И вот эту задачу намного легче решать при копировании, причем сразу объединяя ее с задачей поиска живых объектов:

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

Недостаток такого метода в том, что требуется всегда держать в наличии пустой регион, в который мы будем перемещать живые объекты. Но тут нас выручает слабая гипотеза о поколениях, которую мы рассматривали в прошлой статье. Оказывается, что для вмещения всех живых объектов новой области не нужно быть очень большой, так как подавляющее число объектов в младшем поколении окажутся мусором. В нашем случае новый регион — это пустой Survivor, а старый регион — это Eden + заполненный Survivor. То есть логичнее смотреть на Eden как на дополнение к текущему активному Survivor'у, а не наоборот, тогда все становится на свои места. Мы просто переподключаем Eden каждый раз к новому Survivor'у после очистки младшего поколения.

В итоге мы получаем небольшой оверхед по памяти (пустой Survivor), но зато и высокую скорость работы. Реализация варианта с уплотнением оказывается медленнее. Уплотнение используется при сборках мусора в старшем поколении, так как там уже заведомо старые объекты и мы не можем опираться на гипотезу поколений.
Тема, действительно, интересная. Как позволит время, поразбираемся с проприетарными/коммерческими jvm.
Есть еще Garbage Collection: Algorithms for Automatic Dynamic Memory Management с псевдокодом и сравнениями. В открытом доступе можно найти ее очень плохой скан, но по нему сможете сделать заключение о степени академичности. Мне показалась вполне практичной, но я ее не целиком читал, а только выборочно интересующие меня главы.
Про memory model согласен, убрал из тегов.
Спасибо! Продолжение будет уже более детальным и, надеюсь, интересным не только новичкам. Картинки тоже обещаю, не переключайтесь)

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность