What an object really costs
Every object carries a 12 byte header, and the total is rounded up to a multiple of eight.
Open this lesson in the learning hubKey points
- An object is a header plus its fields. The header is 12 bytes: an 8 byte mark word and a 4 byte class pointer.
- The mark word carries the identity hash and lock state. The class pointer is how the JVM knows what type it is looking at.
- The total is padded to a multiple of 8, so even
new Object()costs 16 bytes. - References are 4 bytes rather than 8 on heaps under 32 GB. That is compressed oops, and it is on by default.
- Arrays add 4 more bytes for the length. Wrappers are mostly overhead: an
Integeris 16 bytes to carry 4 bytes of data. - This is why a million-entry
HashMapcosts many times more than a million-slotint[].
Example
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
public class Main {
static class Point { int x; int y; }
static class Node { Object next; }
static Object sink; // publishing it stops the JIT skipping the allocation
public static void main(String[] args) {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
if (!(bean instanceof com.sun.management.ThreadMXBean sun)
|| !sun.isThreadAllocatedMemorySupported()) {
System.out.println("this VM does not expose allocation counters");
return;
}
System.out.println("bytes on the heap, measured over 100,000 allocations each:");
System.out.println(" new Object() " + bytesEach(sun, 0) + " header only");
System.out.println(" new Node() " + bytesEach(sun, 1) + " header + 1 reference");
System.out.println(" new Point() " + bytesEach(sun, 2) + " header + 2 ints");
System.out.println(" new int[4] " + bytesEach(sun, 3) + " header + length + 4 ints");
System.out.println();
System.out.println("Header is 12 bytes: 8 byte mark word + 4 byte class pointer,");
System.out.println("then the whole object is rounded up to a multiple of 8.");
System.out.println();
System.out.println("identity hash, straight out of the mark word: "
+ Integer.toHexString(System.identityHashCode(new Point())));
}
static long bytesEach(com.sun.management.ThreadMXBean sun, int kind) {
int n = 100_000;
long start = sun.getCurrentThreadAllocatedBytes();
for (int i = 0; i < n; i++) {
switch (kind) {
case 0 -> sink = new Object();
case 1 -> sink = new Node();
case 2 -> sink = new Point();
default -> sink = new int[4];
}
}
return (sun.getCurrentThreadAllocatedBytes() - start) / n;
}
}
Small objects are mostly header. Count objects, not just fields.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.