На 64-битной JVM каждая ссылка на объект — это 64-битный указатель. При большом количестве объектов это заметно увеличивает потребление памяти и снижает эффективность кеша: меньше ссылок помещается в cache line. Compressed OOPs (Ordinary Object Pointers) — механизм, который решает эту проблему, но с нетривиальными последствиями при масштабировании heap.
🔹 Механика
JVM выравнивает объекты по 8 байт. Значит, три младших бита любого указателя всегда равны нулю и их можно не хранить. Если сдвинуть 32-битное значение влево на 3 бита, получим 35 бит адресного пространства — 32GB. Именно поэтому compressed oops работают при heap до 32GB: 32-битная "сжатая" ссылка декодируется в реальный 64-битный адрес сдвигом.
реальный адрес = сжатая_ссылка << 3
При heap ≤ 4GB сдвиг не нужен — просто zero-based, ссылка используется напрямую как смещение от адреса 0.
🔹 Пороговые значения
До 4GB heap: нулевое смещение, нет сдвига. Самый быстрый вариант.
4GB — 32GB: сдвиг на 3 бита, смещение ненулевое.
Выше 32GB: compressed oops отключаются, все ссылки становятся 64-битными.
Здесь ловушка. Если поднять heap с 28GB до 33GB — потребление памяти может вырасти не на 5GB, а значительно больше. Все ссылки удваиваются в размере: с 4 до 8 байт. На приложении с десятками миллионов объектов это легко даёт +20-30% к реальному потреблению.
🔹 Как проверить что включено
java -XX:+PrintFlagsFinal -version | grep UseCompressedOops
Или в логах GC при старте с
-Xlog:gc+heap=info:Heap address: 0x00000006c0000000, size: 28672 MB, Compressed Oops mode: Non-zero based
"Zero based" — heap ≤ 4GB, оптимально.
"Non-zero based" — 4-32GB, compressed oops работают но со сдвигом.
Если строки нет и ссылки 8 байт — compressed oops выключены.
🔹 Compressed Class Pointers
Помимо ссылок на объекты, в заголовке каждого объекта есть ссылка на klass (метаданные класса). Она тоже сжимается отдельным механизмом — Compressed Class Pointers, отдельное 32-битное пространство (Metaspace). Управляется -XX:CompressedClassSpaceSize, по умолчанию 1GB.
🔹 Практический вывод
Граница в 32GB — критическая при планировании capacity. Стандартная рекомендация: если нужно чуть больше 32GB, рассмотреть heap в 26-30GB с compressed oops вместо 33GB без них. Реальная эффективная ёмкость может оказаться выше за счёт лучшей утилизации кеша процессора и меньшего давления на GC.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava