skip to content

Developers often say allocating a small object on the JVM is cheaper than calling malloc in C. Mechanically, what does the JVM do on allocation that makes that plausible, and what does it pay for it later?

level: juniorimportance: should knowfreq 50%

answer

  1. malloc searches free lists; JVM bumps a pointer
  2. copying collector leaves free space contiguous
  3. cost is proportional to survivors, not garbage
  4. zeroing is linear in bytes allocated
  5. allocation schedules a future pause

basics

~20 s

Because a moving young collector leaves free space contiguous, allocation is just advancing a pointer and writing the object header, with no free-list search. The bill arrives later: the collector must find and copy the surviving objects.

solid answer

~60 s

A general allocator such as malloc must manage a heap full of holes: it searches size-class free lists, splits and coalesces blocks, and takes some synchronization for shared structures. Its cost depends on fragmentation history. The JVM's young space is different because the young collector copies live objects out and leaves the space empty and contiguous. Free memory is therefore a single range, and allocation is: read the current free pointer, add the object's size, check it is still inside the range, store the new pointer, then write the two header words and the zeroed fields. In compiled code this is inlined — a few instructions, no search, no locking, since each thread bumps its own private buffer. The cost is deferred rather than removed. Zeroing memory is proportional to bytes allocated, and every allocated byte brings the next young collection closer. Collection cost scales with the amount of data that survives, not with the garbage, so cheap allocation of short-lived objects really is cheap; allocating objects that survive is not.

go deeper

for a junior

Say allocation advances a pointer in a per-thread chunk, and that objects dying young are cheap because the collector only copies survivors.

for a middle

Contrast with free-list allocation, name zeroing as a real cost, and state the allocation-rate to collection-frequency relationship.

for a senior

Turn it into guidance: attack survivor volume and allocation rate separately, and recognize memory bandwidth as a limit.

for a principal

Discuss the tradeoff of moving collectors overall — cheap allocation and compaction paid for with copying, barriers, and pause structure.

## Two different allocation problems A C-style allocator must satisfy requests from a heap that has been carved up by an arbitrary history of allocations and frees. It keeps free lists (usually per size class), searches one for a block that fits, may split a larger block, and on free may coalesce neighbours. It also has to be thread-safe, which in practice means per-thread caches plus locks on shared arenas. The work per call is small but variable, and fragmentation makes it worse over time. The JVM has arranged to avoid that problem for the common case. Its young space is collected by a copying (evacuating) collector: at each young collection the live objects are copied to survivor space or promoted, and the collected area is then wholly free. So there are never holes to track — free memory is one contiguous stretch. ## The fast path With contiguous free space, allocation reduces to arithmetic on a single pointer: the object goes at the current free pointer, and the pointer moves forward by the object's size. That is called bump-the-pointer allocation. To avoid making that pointer a contention hot spot, each thread bumps its own private slice of the young space, so the sequence needs no atomic instruction. The JIT compiler inlines the whole thing into the caller, so a `new` for a small class with a statically known size becomes roughly: load pointer, add constant, bounds check, store pointer, store mark word, store class pointer, done. A nuance often missed: object size must be known to do this. For a class it is fixed at class-load time; for an array it is computed from the length, which is why array allocation includes a length check and a size multiplication. ## What the JVM pays Three bills come due. **Zeroing.** The Java language guarantees fields start at 0, false, or null, so the memory must be cleared. Whether done in bulk when a buffer is handed out or per object, the cost is linear in bytes allocated. High allocation rates are partly a memory-bandwidth problem for this reason. **Collection frequency.** Bytes allocated per second divided by the size of the young space gives roughly how many young collections happen per second. Allocation does not cost time now; it schedules a pause later. **Survivor cost.** A copying collector's work is proportional to what survives, not to what was allocated. Objects that die before the next young collection cost essentially nothing to reclaim — they are simply not copied. Objects that survive are copied, possibly repeatedly, and eventually promoted, which is the expensive path. ## Why the comparison is still not a blank cheque Malloc does not stop your program later; the JVM's deferred cost shows up as pauses and as CPU spent in collector threads. And a manual allocator does not have to zero memory. The honest statement is: the JVM's allocation *operation* is cheaper and scales better across threads, and reclamation of short-lived data is nearly free, but total memory management cost is paid across allocation, zeroing, and collection, and is dominated by how much data survives. ## Practical consequence This is why the standard advice is not do not allocate but do not allocate objects that survive unnecessarily and do not allocate at a rate that saturates memory bandwidth. Small, short-lived, thread-confined objects are exactly the case the machinery is optimized for.

  • If short-lived garbage is nearly free to collect, why does a very high allocation rate still hurt latency?
    Because it drives collection frequency. Even if each young collection is short, filling the young space many times per second means many pauses, and each one still has fixed costs such as root scanning and thread stopping. High allocation also consumes memory bandwidth for zeroing and copying, which slows the application itself.
  • What determines whether the allocated object dies cheaply or becomes expensive?
    Whether it is still reachable when the next young collection runs. If it is, it must be copied, and if it keeps surviving it is eventually promoted to the old generation, where reclaiming it needs a more expensive collection. So object lifetime relative to the young-collection interval, not object size alone, decides the cost.

Malloc is finding a free seat in a half-full theatre; young-space allocation is seating people left to right in a theatre that was emptied a moment ago.

saying these in an interview costs you the question

  • Saying JVM allocation is free because the garbage collector handles memory.
  • Claiming garbage objects are individually visited and freed — a copying young collector only touches survivors.
  • Assuming pointer-bump allocation works on any heap layout; it depends on free space being contiguous, which a moving collector provides.
  • Ignoring zeroing entirely when explaining allocation cost.

context