V8 handles large objects by allocating them in a dedicated 'Large Object Space' (LOS) where each object occupies its own memory region and is never moved by the garbage collector, though they are still subject to normal garbage collection when unreachable.
Large Object Space is one of V8's heap spaces, alongside New Space, Old Space, Code Space, and Map Space . Objects exceeding a size threshold (typically 128KB - 256KB) are allocated directly in LOS rather than in regular pages . This design choice stems from the fact that moving large objects during garbage collection would be prohibitively expensive in terms of CPU time and memory bandwidth. Instead, each large object gets its own mmap'd region of memory and remains in place for its entire lifetime .
Size Threshold: Objects larger than Page::kMaxRegularHeapObjectSize (typically 128KB-256KB) are allocated in LOS. This threshold is defined in V8's heap spaces header .
Individual Memory Regions: Each large object receives its own dedicated mmap'd memory region, unlike regular spaces where multiple objects share pages .
No Moving Collection: Large objects are never moved or compacted by the garbage collector, eliminating the cost of copying large memory blocks .
Still Garbage Collected: Despite being immovable, large objects are still subject to normal garbage collection—when they become unreachable, their memory is freed .
Part of Old Generation: For memory limit purposes, LOS is considered part of the old generation and counts against --max-old-space-size .
Fragmentation: Since large objects aren't moved, LOS can become fragmented over time. However, because each object has its own region, fragmentation is less problematic than in regular spaces .
Allocation Speed: Allocating large objects requires finding contiguous virtual memory via mmap, which is slower than bump-pointer allocation in young generation .
GC Marking: During mark phase, large objects are traversed like any other object—if they're reachable, they're marked; if not, their entire memory region is released .
Pointer Updates: Because large objects never move, no pointer updates are needed for references to them, simplifying remembered set management .
It's important to distinguish between V8's managed heap and off-heap memory. Objects like Node.js Buffers allocate memory outside V8's heap (via C++ malloc) and are not subject to V8's garbage collection in the same way. This explains why a process can use 10GB+ memory even with a modest --max-old-space-size—those allocations bypass LOS entirely . For true large objects managed by V8, LOS provides a pragmatic compromise: avoid expensive copying while still participating in the normal garbage collection lifecycle.
No Separate Limit: LOS cannot be configured independently—its size is included in --max-old-space-size .
Threshold Tuning: The large object threshold can be adjusted in V8 builds (modifying kMaxRegularHeapObjectSize in spaces.h), but this is not exposed as a runtime flag .
Monitoring: Use --trace-gc to see when large objects are allocated and collected. Heap snapshots show objects in LOS with their sizes.
Process Memory: Remember that process.memoryUsage().heapUsed includes LOS, but off-heap memory (Buffers, external strings) appears in external .
In practice, LOS handles objects like large arrays, long strings, and ArrayBuffers that exceed the page size threshold. The design reflects a fundamental trade-off in garbage collection: the cost of moving large objects outweighs the benefits of compaction, so V8 opts for non-moving allocation with per-object memory regions. This approach ensures that applications with large data structures can still perform well, avoiding the severe pause times that would result from repeatedly copying megabytes of data during GC cycles .