Задавались ли вы когда-нибудь вопросом, как выглядят java объекты изнутри?
Под катом будет подробное описание заголовка java объекта, из чего он состоит и сколько занимает памяти.


Для начала вспомним, что в jvm любой объект в памяти состоит из заголовка объекта и переменных объекта (ссылки и примитивы). Также финальный размер объекта может быть расширен, чтобы размер стал кратен 8 байтам.


Заголовок каждого объекта (кроме массива) состоит из двух машинных слов — mark word и сlass word. Массивы имеют дополнительно 32 бита для описания длины массива.


В Mark word хранится identity hashcode, биты используемые сборщиком мусора и биты используемые для блокировок. Более подробно можно всегда ознакомиться в соответствующих сорцах OpenJDK markOop.hpp.


В Class word хранится указатель на сам класс, то есть, на то место, где лежит информация о данном типе данных: методы, аннотации, наследование и другое. Более подробно можно также всегда ознакомиться в соответствующих сорцах OpenJDK klass.hpp.


Давайте теперь более подробно рассмотрим заголовок объекта и в частности mark word


32 битные jvm


32 bit JVM


Как видно из таблицы, содержание Mark word может сильно разниться в зависимости от текущего состояния объекта.


Нормальное состояние объекта (biased_lock = 0, lock = 01)


  • identity_hashcode — хеш объекта, который появляется лениво. Если у объекта будет впервые вызов System.identityHashCode(obj), то этот хеш будет рассчитан и записан в заголовок объекта.
    В других состояниях, когда за объект конкурируют различные потоки, identity_hashcode будет храниться уже не в заголовке объекта, а в мониторе объекта.
  • age — количество пережитых сборок мусора. Когда age достигает числа max-tenuring-threshold,
    объект перемещается в область хипа old generation.
  • biased_lock — содержит 1, если biased locking включено для этого объекта, иначе 0.

Немного подробней
Когда Biased Locking включено, объект как бы смещается к первому объекту, захватившему его монитор. Последующий захват объекта тем же потоком будет немного быстрее.

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


Biased Locking по умолчанию включено начиная с java 6, -XX:-UseBiasedLocking

  • lock — содержит код состояние блокировки. 00 — Lightweight Locked, 01 — Unlocked or Biased, 10 — Heavyweight Locked, 11 — Marked for Garbage Collection.

То есть в таблице состояние объекта определяют совокупность битов biased_lock и lock.


Режим Biased Locked (biased_lock = 1, lock = 01)


  • thread — в режиме biased блокировки предполагается, что объектом преимущественно владеет какой-то конкретный поток, в поле хранится id этого потока.
  • epoch содержит некоторый временной индикатор владения объектом потоком, id которого сохранено в thread

Режим Lightweight Locked (lock = 00)


В этом режиме предполагается, что время захвата данным объектом разными потоками не пересекается вообще или пересекается незначительно. В этом режиме вместо тяжеловесных блокировок операционной системы, JVM использует атомики.


  • ptr_to_lock_record — для установки/ожидания блокировки используется CAS (compare and set) внутри цикла spin loop.
    Для справки, минимальное время блокировки ОС будет в районе порядка 10 мс, при помощи атомиков поток не засыпает, а продолжает молотить небольшой цикл, и как только ресурс освободится, цикл с атомиком закончится, и поток тут же захватит этот объект.

Режим Heavyweight Locked (lock = 10)


  • ptr_to_heavyweight_monitor — если время захвата данным объектом различными потоками будет значительно пересекаться, то lightweight lock будет заменена на heavyweight lock. В ptr_to_heavyweight_monitor будет записан указатель на монитор. Используется блокировка ОС.

Итак в 32 битной jvm хедер объекта состоит из 8 байт. Массивы дополнительно имеют 4 байта.


64 битные jvm


64 bit JVM


На 64 битной jvm хедер объекта состоит из 16 байт. Массивы дополнительно имеют 4 байта.


64 битные jvm с сжатием указателей


64 bit JVM  Coops


Хедер объекта состоит из 12 байт. Массивы дополнительно имеют 4 байта.


Немного о сжатии указателей. Для 32 битного указателя адресное пространство ограничено 4Гб. Однако если снова вспомнить, что в jvm размер объекта кратен 8 байтам, то мы можем использовать псевдо 35 битный указатель, с тремя нулями на конце. И, таким образом, ссылаться уже на 32Гб памяти. Сжатие получается не бесплатное, цена — дополнительная операция (pointer << 3) при любом обращении в heap.


Ссылка на оригинальную статью:


Java Object Header


Также хотел бы добавить, что всё здесь описанное не является догмой, возможно, в других версиях jvm заголовок объекта будет отличаться. Описанное тут актуально для openjdk 8.

Комментарии (1)


  1. easty
    16.04.2019 18:42

    Тут ещё можно уточнить про сжатие указателей, если jvm "сожрёт" больше 32гб, все ссылки автоматически переконвертятся и сразу jvm увеличит свой хип в 1.5 раза. Важный момент в тюнинге jvm