Получить доступ

Память JVM и устройство объекта

Runtime data areas, стек против кучи, заголовок объекта и выравнивание, compressed oops и metaspace.

Runtime data areas

JVM делит память на области. Heap — общая для всех потоков, там живут все объекты и массивы. У каждого потока свои: JVM stack (кадры методов), program counter и, для нативных вызовов, native stack. Metaspace (вне кучи) хранит метаданные классов, code cache — скомпилированный JIT-код.

Heap собирается сборщиком мусора; стек освобождается автоматически при возврате из метода (LIFO). Переполнение стека — StackOverflowError (обычно глубокая рекурсия), нехватка кучи — OutOfMemoryError.

Области памяти JVM: куча, метапространство, стеки потоков, счётчик команд; справа — устройство объекта: заголовок, указатель на класс, поля.
Области памяти времени выполнения JVM и устройство одного объекта в куче.

Стек против кучи

В кадре стека лежат примитивные локальные переменные и ссылки; сами объекты — всегда в куче. Поэтому Account a = new Account() кладёт ссылку в стек, а объект — в кучу.

JIT может через escape analysis доказать, что объект не «убегает» из метода, и применить scalar replacement — разложить его на локальные переменные и не аллоцировать в куче вовсе. Это, а не «маленькие объекты на стеке», и есть реальная оптимизация аллокаций.

void m() {
    int x = 5;                 // примитив в кадре стека
    Account a = new Account(); // ссылка в стеке, объект в куче
}

Устройство объекта в памяти

Объект в куче состоит из заголовка и полей. Заголовок — это mark word (хеш-код по требованию, биты блокировки/состояния GC, возраст для tenuring) и klass pointer (ссылка на метаданные класса в metaspace). У массивов в заголовке ещё длина.

Поля выравниваются, а весь объект дополняется до кратности 8 байт (padding) для эффективного доступа. Поэтому пустой объект занимает не ноль, а обычно 16 байт — это объясняет, почему миллионы крошечных объектов дороги по памяти.

Compressed oops

На 64-битной JVM ссылки (oops) по умолчанию сжимаются до 32 бит, пока куча меньше ~32 ГБ. Это вдвое уменьшает размер ссылок и заметно экономит память и нагрузку на кэш.

Практическое следствие: куча чуть выше 32 ГБ может вмещать меньше полезных данных, чем 31 ГБ, потому что отключаются compressed oops — частый сюрприз при тюнинге.

Metaspace

Метаданные классов с Java 8 живут в metaspace — нативной памяти вне кучи, которая растёт автоматически (в отличие от старого PermGen фиксированного размера). Это убрало классический «OutOfMemoryError: PermGen space».

Но утечки классов всё ещё возможны: например, постоянная перезагрузка классов через новые загрузчики (динамическая генерация, частый redeploy) раздувает metaspace — его ограничивают -XX:MaxMetaspaceSize.

Как отвечать на собеседовании

Назовите области (heap общий, стеки/PC по потоку, metaspace вне кучи), объясните «примитивы и ссылки в кадре, объекты в куче» и упомяните escape analysis вместо мифа «объекты на стеке». Бонус-уровень — заголовок объекта (mark word + klass pointer), padding до 8 байт и compressed oops с порогом ~32 ГБ.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем