Why Java Objects Need Memory Alignment: Deep Dive into JVM Layout, False Sharing, and Compressed Oops
This article explains how Java objects are laid out in the JVM heap, why fields are reordered and padded for alignment, how false sharing degrades performance, and how the @Contended annotation and compressed oops help mitigate these issues, all backed by detailed diagrams and code examples.
Hello everyone, I'm bin. It's time for our weekly meeting. My public account published the first article on January 10, "Evolution of IO Models from a Kernel Perspective" which attracted over 2,000 reads and 80 likes. Today I bring another technical deep‑dive, this time from the perspective of computer architecture, explaining how objects are laid out in JVM memory, what memory alignment is, the consequences of ignoring it, and the principles behind compressed pointers. We also cover false sharing in high‑concurrency scenarios and its solutions.
1. Java Object Memory Layout
In the JVM a Java object is represented by an instanceOopDesc structure and its memory layout consists of three parts: the object header, the class pointer, and the instance data.
1.1 Object Header
Every object contains a header that stores two kinds of information: MarkWord: stored in a markOopDesc structure, it holds runtime data such as hash code, GC generation age, lock state, biased thread ID, etc. It occupies 4 bytes on a 32‑bit OS and 8 bytes on a 64‑bit OS. Class Pointer: stored in a klassOopDesc structure, it points to an InstanceKlass object that contains class metadata (inheritance, methods, static fields, constructors). Without compressed pointers it occupies 4 bytes on 32‑bit and 8 bytes on 64‑bit; with -XX:+UseCompressedOops it occupies 4 bytes on both.
If the object is an array, the header also contains a 4‑byte field that records the array length.
1.2 Instance Data
The instance data area stores all instance fields defined in the class and its super‑classes. Primitive types occupy fixed sizes (long/double 8 B, int/float 4 B, short/char 2 B, byte/boolean 1 B). Reference types occupy 4 B with compressed pointers and 8 B without.
2. Field Reordering
When Java source code declares fields in a certain order, the JVM may reorder them to satisfy alignment rules and improve memory usage. The reordering is controlled by the -XX:FieldsAllocationStyle flag (default 1) and follows three rules:
If a field occupies X bytes, its offset must be aligned to NX.
In a 64‑bit JVM with compressed pointers, the first field of a class must be aligned to 4 N; without compressed pointers it must be aligned to 8 N.
Fields from a superclass appear before fields of a subclass, and within the same class long/double fields precede int/float, which precede short/char, which precede byte/boolean, followed by reference fields.
When -XX:+CompactFields is enabled (default), fields smaller than a long/double may be inserted into gaps before the first long/double to avoid unnecessary padding.
Example:
public class Parent {
long l;
int i;
}
public class Child extends Parent {
long l;
int i;
}Depending on the combination of -XX:+UseCompressedOops and -XX:+CompactFields, the layout of Child objects varies:
2.1 -XX:+UseCompressedOops -XX:-CompactFields (compressed pointers, field compression disabled)
The object header occupies 12 bytes (MarkWord 8 B + class pointer 4 B). Padding is added so that the first long field aligns to an 8‑byte boundary, resulting in a total size of 48 bytes after object‑level padding.
2.2 -XX:+UseCompressedOops -XX:+CompactFields (compressed pointers, field compression enabled)
With field compression, the int field from the superclass can be placed before the first long, reducing the object size to 40 bytes.
2.3 -XX:-UseCompressedOops -XX:-CompactFields (no compressed pointers, no field compression)
Without compressed pointers the class pointer occupies 8 B, making the object header 16 B. Padding aligns the long fields to 8‑byte boundaries, resulting in a 48 byte object after final padding.
2.4 -XX:-UseCompressedOops -XX:+CompactFields (no compressed pointers, field compression enabled)
Even with field compression, the larger class pointer prevents any size reduction; the object remains 48 bytes.
3. Alignment Padding
Padding not only occurs between fields but also between objects. The JVM aligns object start addresses to 8‑byte boundaries (controlled by -XX:ObjectAlignmentInBytes, default 8). If an object’s size is not a multiple of 8, extra bytes are added at the end.
4. Applications of Alignment Padding
Beyond reducing wasted space, padding is used to solve false sharing, a performance problem where unrelated variables share the same cache line.
4.1 CPU Cache Basics
Modern CPUs have multiple cache levels (L1, L2, L3). L1 is private to a physical core, L3 is shared among all cores. Each cache line is typically 64 bytes. When a thread writes to a volatile variable, the CPU issues a lock prefix and the cache‑coherency protocol (MESI) ensures visibility, but if two volatile fields reside on the same cache line, updates to one invalidate the other’s cache line, causing performance degradation.
False Sharing Example
public class FalseSharing {
volatile long a;
volatile long b;
}Fields a and b are adjacent in memory and may occupy the same 64‑byte cache line. When thread A updates a, the cache line is locked and invalidated in other cores, preventing thread B from efficiently reading b. This leads to unnecessary memory traffic and reduced throughput.
4.2 Padding Solutions
Before Java 8, developers manually added padding fields (seven dummy longs) before and after the volatile fields to force each onto a separate cache line, increasing object size from ~32 B to ~200 B.
public class FalseSharing {
long p1,p2,p3,p4,p5,p6,p7;
volatile long a;
long p8,p9,p10,p11,p12,p13,p14;
volatile long b;
long p15,p16,p17,p18,p19,p20,p21;
}Java 8 introduced the @Contended annotation, which automatically adds the necessary padding (default 128 bytes) without manual fields. It can be applied to a class (entire instance data) or to individual fields. Padding width can be tuned with -XX:ContendedPaddingWidth. The annotation is effective only when the JVM flag -XX:-RestrictContended is enabled.
@Contended on a Class
@Contended
public class FalseSharing {
volatile long a;
volatile long b;
volatile int c;
volatile int d;
}The JVM adds 128 bytes before and after the instance data, ensuring the whole object occupies its own cache lines.
@Contended on Fields
public class FalseSharing {
@Contended volatile long a;
@Contended volatile long b;
volatile int c;
volatile long d;
}Only a and b receive separate cache‑line padding.
Contended Groups
public class FalseSharing {
@Contended("group1") volatile int a;
@Contended("group1") volatile long b;
@Contended("group2") volatile long c;
@Contended("group2") volatile long d;
}Fields in the same group may share a cache line; different groups are padded apart.
5. Why Memory Alignment Matters
5.1 Speed
CPU reads/writes in word‑size units (8 bytes on 64‑bit). Aligned addresses can be fetched with a single read transaction; misaligned addresses require two transactions, effectively halving throughput.
5.2 Atomicity
Operations on a naturally aligned word can be performed atomically by the CPU.
5.3 Cache Utilization
Aligning objects and fields to 8‑byte boundaries increases the chance that they fit within a single 64‑byte cache line, avoiding the performance penalty of spanning two cache lines.
5.4 Enabling Compressed Oops
Because heap objects are 8‑byte aligned, the three low bits of a 64‑bit address are always zero. Compressed oops store only the high 32 bits and shift left by three bits at runtime, expanding the addressable heap from 4 GB to 32 GB (4 GB × 8). The alignment also allows the JVM to use the low bits for additional address bits, extending the range further if -XX:ObjectAlignmentInBytes is increased (e.g., alignment 16 gives 64 GB).
6. Compressed Pointers in Detail
When -XX:+UseCompressedOops is on, object references are 32 bits. At allocation time the JVM records the object’s address divided by 8 (i.e., shifted right 3 bits). When the reference is dereferenced, it is shifted left 3 bits, restoring the original 64‑bit address. This saves 4 bytes per reference and reduces overall heap pressure.
7. Array Object Layout
7.1 Primitive Arrays
Primitive arrays are represented by a typeArrayOop structure. Their header contains the usual MarkWord, class pointer, and an additional 4‑byte length field. The instance data follows, consisting of the raw primitive elements. long[] longArray = new long[1]; With compressed oops the layout is: MarkWord 8 B + class pointer 4 B + length 4 B + data 8 B = 24 B (padded to 24 B, already 8‑byte aligned). Without compressed oops the class pointer becomes 8 B, requiring 4 B of padding before the data, resulting in a 32 B object.
7.2 Reference Arrays
Reference arrays use objArrayOop. Each element is a reference (4 B with compressed oops, 8 B without). The header is the same as primitive arrays.
public class Ref {
char a;
int b;
short c;
}
Ref[] refArray = new Ref[1];With compressed oops the instance data is 4 B (one reference) plus 4 B padding to keep the whole object 8‑byte aligned (total 24 B). Without compressed oops the reference occupies 8 B, and 4 B of padding is inserted after the header, yielding a 32 B object.
8. Summary
This article presented a thorough examination of Java object and array memory layouts, the rules governing field reordering, the impact of alignment padding, and how false sharing can degrade performance. It also described the @Contended annotation introduced in Java 8, the reasons behind memory alignment (speed, atomicity, cache efficiency, and enabling compressed oops), and detailed how compressed pointers expand the usable heap while halving reference size. Finally, we covered relevant JVM flags such as -XX:+UseCompressedOops, -XX:+CompactFields, -XX:-RestrictContended, -XX:ContendedPaddingWidth, and -XX:ObjectAlignmentInBytes. Thank you for reading, and see you in the next article!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Bin's Tech Cabin
Original articles dissecting source code and sharing personal tech insights. A modest space for serious discussion, free from noise and bureaucracy.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
