docs/heap/heap-overview.md
V8 manages its own heap memory, organized into several "spaces" to optimize allocation and garbage collection.
Most spaces in the V8 heap are divided into Pages (typically 256KB or 512KB depending on architecture).
MemoryAllocator and belong to specific spaces.Every object allocated on the V8 heap (a HeapObject) has a common header structure. The very first word of every HeapObject is a tagged pointer to a Map (also known as a hidden class or shape).
The Map is crucial because it contains metadata that allows V8 to understand the object's structure without inspecting the object itself:
InstanceType (e.g., JS_OBJECT_TYPE, STRING_TYPE, FIXED_ARRAY_TYPE) identifies what kind of object it is.instance_size tells V8 how many bytes the object occupies.visitor_id tells the garbage collector how to iterate over the pointers contained within the object body (e.g., which fields are tagged pointers vs raw data).
BodyDescriptor classes (e.g., JSObject::BodyDescriptor) to provide a static way to iterate over object bodies. The visitor_id maps to these descriptors to guide the GC without needing virtual calls for every object.Consider a simple JavaScript object:
const obj = { a: 1, b: 2 };
In the heap, this JSObject might look like this:
digraph G {
rankdir=LR;
newrank=true;
node [shape=record, style=filled];
jsobject [label="<head> JSObject | <map> Map Pointer | <props> Properties Pointer | <elems> Elements Pointer | In-Object 'a': 1 | In-Object 'b': 2"];
map [label="<head> Map | InstanceType: JS_OBJECT_TYPE | InstanceSize: 32 bytes | VisitorID: JSObjectVisitor | DescriptorArray"];
empty_array [label="EmptyFixedArray", shape=ellipse];
jsobject:map -> map:head;
jsobject:props -> empty_array;
jsobject:elems -> empty_array;
}
Every heap access starts by reading the Map to know how to handle the rest of the object!
V8 divides the heap into several spaces: