Java Object Structure

When a Java program runs on the HotSpot virtual machine, an object is more than a container for field values. It also carries runtime metadata that supports identity checks, locking, garbage collection, and class resolution. The way HotSpot divides and pads this memory can affect performance, object sizing, and GC behavior.

Most Java objects in heap memory are viewed as three main components: the object header, instance data, and alignment padding.

  • Object header: stores metadata needed by the runtime.
  • Instance data: holds the actual field values.
  • Alignment padding: ensures the final object size meets alignment rules.

Understanding the Object Header

The object header is a critical piece of the structure. In 64-bit HotSpot, the mark word usually occupies 8 bytes. It holds information such as hash codes, garbage collection ages, lock state flags, biased lock details, and timestamps. Since these meanings can change over time, the same memory may serve different purposes depending on the current state.

The klass pointer follows the mark word. It points to class metadata in the method area, helping the JVM identify the object’s type. With compressed oops enabled, the klass pointer may use only 4 bytes; otherwise, it may use 8 bytes.

Arrays include an additional field: the array length. A regular object does not have this field.

With compressed oops enabled, a normal object header is often 12 bytes. An array header may be 16 bytes because it includes the array length.

Instance Data and Field Layout

Instance data stores field values, including fields inherited from superclasses. HotSpot generally allocates superclass fields before subclass fields. Fields with similar widths may be grouped together.

The default ordering is not simply the declaration order. Long and double values tend to come first, followed by int and float, then short and char, then byte and boolean, with references placed afterward.

Reference size depends on compressed oops. When enabled, a reference may take 4 bytes; when disabled, it may take 8 bytes.

The -XX:+CompactFields flag can influence how fields are packed, but application developers rarely control this directly.

Declaration order does not always match runtime memory order. HotSpot may reorder fields to improve alignment and reduce wasted space.

Alignment Padding

HotSpot requires object alignment to 8 bytes. If the header plus instance data does not form a multiple of 8, padding is added.

For example, a header of 12 bytes plus 4 bytes of data totals 16 bytes, so no padding is needed. A header of 12 bytes plus 8 bytes of data totals 20 bytes, requiring 4 bytes of padding to reach 24 bytes.

This step is small but important when calculating actual object size.

Object Creation and Access

Object creation typically begins with the new instruction, which checks that the class is loaded, linked, and initialized. Memory allocation may use collision, free list, or thread-local allocation buffer methods. After allocation, memory may be zeroed, the header set, and the constructor executed.

There are two common access approaches:

  • Handle-based access: a reference points into a handle pool, and the handle points to the instance and type data.
  • Direct pointer access: a reference points directly to the object, and the header’s klass pointer points to the type data.

HotSpot generally uses direct pointers to reduce extra indirection.

Checking Object Size with JOL

The OpenJDK JOL tool can inspect object layout. A typical dependency entry looks like this:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>

A simple class with an int field and a String field can be measured through JOL. On a 64-bit JVM with compressed oops, the output may show a 12-byte header, a 4-byte integer, a 4-byte reference, and then alignment padding.

In one example, the raw header and field sizes added to 20 bytes, so padding brought the final object size to 24 bytes.

Why Object Structure Matters

The layout of a Java object influences how much memory it uses and how the JVM behaves under load. The mark word connects closely to locking and garbage collection. The klass pointer helps resolve types. Instance data holds field values. Alignment padding satisfies memory boundaries.

By understanding these pieces, developers can make better decisions about references, inheritance, and object design. Tools such as JOL can help measure real layouts, while careful coding can reduce unnecessary memory overhead.