TLABs: allocation is a pointer bump
Each thread owns a private slab of eden, so new is an add and a store, with no lock anywhere.
Open this lesson in the learning hubKey points
- Eden is handed out in TLABs: every thread gets a private chunk, so two threads never contend on the same allocation pointer.
- Allocating inside a TLAB is one pointer bump plus zeroing the object. It is a handful of instructions, not a call into the VM.
- When a TLAB runs out the thread claims a fresh one from eden. Its size adapts to how fast that particular thread allocates.
- Objects too big for a TLAB go straight into shared eden, and in G1 anything over half a region becomes a humongous old-gen object.
- So allocation rate, not allocation cost, is what shows up in a profile. The bill arrives at the next young collection.
- Escape analysis can delete the allocation altogether, which is the only thing cheaper than a pointer bump.
Example
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
public class Main {
static class Order { int id; long total; }
static Object sink; // published, so the JIT cannot delete the allocation
public static void main(String[] args) {
for (int i = 0; i < 200_000; i++) { sink = new Order(); } // let the JIT warm up
long gcBefore = collections();
int n = 20_000_000;
long t0 = System.nanoTime();
for (int i = 0; i < n; i++) { sink = new Order(); }
long ns = System.nanoTime() - t0;
long gcAfter = collections();
System.out.printf("%,d Order objects in %,d ms%n", n, ns / 1_000_000);
System.out.printf("about %.1f ns each - that is a bump of the TLAB pointer%n", (double) ns / n);
System.out.printf("24 bytes each, so roughly %,d MB of garbage was produced%n", (24L * n) >> 20);
System.out.println("collections that ran while it happened: " + (gcAfter - gcBefore));
System.out.println();
System.out.println("Allocation itself is nearly free: no lock, no free list, no malloc.");
System.out.println("The cost is the collection it eventually causes - and almost every");
System.out.println("one of these objects was already dead when that collection ran.");
}
static long collections() {
long total = 0;
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
long c = gc.getCollectionCount();
if (c > 0) { total += c; }
}
return total;
}
}
Allocation is a pointer bump. The bill arrives at the next young GC.
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.