Как расширение адресного пространства heap может увеличить размер объектов в 64-битной JVM?
В 64-битной JVM увеличение максимально допустимого heap может привести к отключению compressed oops — сжатых ссылок на объекты. Тогда ссылка обычно занимает 8 байт вместо 4, из-за чего увеличиваются заголовки объектов, поля-ссылки, элементы массивов ссылок и связанные с ними расходы памяти.
Это не означает автоматическое удвоение размера каждого объекта: итог зависит от его полей, выравнивания и выбранного JVM режима адресации. Точный порог переключения зависит от версии JVM, платформы, выравнивания heap и настроек.
При переходе к 64-битным JVM адреса объектов стали занимать больше места, хотя приложению обычно не требовалось адресовать весь огромный диапазон 64-битных адресов. Это увеличивало footprint объектов и ухудшало эффективность кэшей процессора.
Compressed oops появились как компромисс: JVM хранит ссылку не как полный машинный адрес, а как более короткое смещение, которое при обращении преобразуется в адрес объекта. Так сохраняется возможность использовать большой heap без обязательной цены полноразмерных ссылок.
Предположим, приложение хранит то же количество объектов, но ему увеличили максимальный размер heap. Если после этого JVM выбрала другой режим адресации, объекты могут стать крупнее даже при неизменном количестве данных.
Последствия — рост фактического потребления heap, большее давление на кэши CPU, увеличение объёма копируемых и сканируемых ссылок во время GC. Поэтому увеличение лимита heap не всегда означает пропорциональное увеличение полезной вместимости приложения.
В обычном 64-битном режиме ссылка на объект занимает 64 бита. При включённых compressed ordinary object pointers ссылка хранится в сжатом виде, часто в 32 битах, а JVM восстанавливает адрес с использованием базы и масштаба, связанными с выравниванием объектов.
Сжатие может применяться не только к обычным ссылкам, но и к ссылкам на метаданные классов — compressed class pointers. Поэтому изменение режима способно затронуть заголовки объектов и поля, содержащие ссылки. Дополнительный эффект дают правила выравнивания: после расширения ссылок JVM может вставить больше padding, чтобы сохранить требуемое выравнивание.
Режим выбирается при запуске JVM на основании доступной адресной схемы, максимального размера heap, архитектуры, версии JVM и других ограничений реализации. Нельзя надёжно выводить порог только из значения размера heap: он не является универсальной константой для всех HotSpot и платформ.
Важно различать зарезервированный heap и реально занятые объекты. Переключение режима влияет прежде всего на layout объектов и потенциальный размер heap, а не обязательно означает немедленное физическое выделение всей памяти. Проверять ситуацию следует по параметрам запуска JVM, логам и диагностике layout объектов, а не только по RSS процесса.
Типичные варианты решения:
Сервис хранит большой граф объектов. После увеличения максимального heap фактическое число объектов не изменилось, но heap стал заполняться быстрее. Профилирование показало рост среднего размера объектов, содержащих много ссылочных полей.
Рассматривались увеличение лимита памяти, уменьшение количества объектов и возврат к меньшему максимальному heap. Простое увеличение лимита только отодвигало проблему и повышало стоимость GC, а массовая переделка модели данных была рискованной.
Выбранное решение — проверить выбранный JVM режим и ограничить максимальный heap значением, при котором сохранялись сжатые ссылки, оставив резерв для естественных пиков нагрузки. Это уменьшило footprint объектов; компромиссом стал меньший доступный запас heap, поэтому лимит согласовали с измеренным профилем нагрузки, а не выбрали произвольно.
Вопрос: Отключение compressed oops увеличивает размер только самих ссылок?
Ответ: Нет. Увеличиться могут поля-ссылки, элементы массивов ссылок, части заголовка объекта, связанные с compressed class pointers, а также padding из-за выравнивания. Для объекта без ссылочных полей эффект может быть небольшим или отсутствовать, если layout не изменился существенно.
Вопрос: Если heap фактически заполнен лишь наполовину, почему JVM не может всегда использовать сжатые ссылки?
Ответ: Возможность сжатия определяется не только текущей занятостью, а адресным диапазоном, который JVM должна уметь адресовать в выбранной конфигурации. Максимальный размер heap и способ его размещения влияют на то, помещаются ли ссылки в представление с выбранной разрядностью и масштабом. Поэтому свободная память внутри heap сама по себе не гарантирует сохранение сжатого режима.
Вопрос: Можно ли по одному росту RSS доказать, что отключились compressed oops?
Ответ: Нет. RSS включает heap, нативную память JVM, области потоков, библиотеки, page cache и другие компоненты. Кроме того, RSS может расти из-за изменения committed-памяти, поведения аллокатора или GC. Гипотезу о layout объектов нужно подтверждать параметрами запуска и диагностикой выбранного режима, а затем сопоставлять с изменением размеров объектов и heap.