How Lodash fill Manages Memory with Object References
When using Lodash's _.fill to populate a large array
with a single object, JavaScript stores references rather than creating
distinct object clones. Because _.fill relies on shallow
assignment, memory consumption is restricted almost entirely to the
array's internal pointer table, while the object itself occupies memory
only once on the heap. This makes the operation extremely
memory-efficient, though it introduces shared-state implications where
modifying any element affects all positions in the array.
Reference Assignment Mechanics
The _.fill method works similarly to the native
Array.prototype.fill. Internally, it iterates from the
specified start index to the end index, assigning the provided value via
standard assignment:
array[index] = value;In JavaScript, objects are reference types. When you pass an object
into _.fill, the engine passes a pointer pointing to the
memory address of that object in the heap. Lodash does not perform deep
cloning or instantiate new instances during each iteration; it writes
the identical memory address into every index of the array.
Heap Memory Allocation
When filling an array of 1,000,000 elements with a single object reference:
- The Object Allocation: Only one instance of the object exists on the V8 heap. The memory cost of the object's properties, keys, and values is incurred exactly once.
- The Array Backing Store: The JavaScript engine allocates a contiguous block of memory (an elements vector) to hold the pointers. On modern 64-bit systems with V8 pointer compression enabled, each reference takes 4 bytes (or 8 bytes without compression).
Consequently, an array with one million slots referencing a single object consumes roughly 4 to 8 megabytes for the pointer buffer, plus a negligible few bytes for the single object. In contrast, deep-cloning an object 1,000,000 times would consume hundreds of megabytes or gigabytes, potentially triggering garbage collection pauses or out-of-memory errors.
Garbage Collection and Lifecycle
Because there is only one object instance, the engine's garbage collector (GC) treats the array as having one million incoming references pointing to that single heap location.
- GC Pressure: Allocating references is light on the GC. There are no thousands of short-lived objects allocated into the nursery (Young Generation), meaning the GC does not have to perform heavy scavenge cycles.
- Deallocation: The single object cannot be garbage-collected as long as the array itself is reachable. Once the array is dereferenced or goes out of scope, the single object also becomes eligible for garbage collection, provided no other external references to it exist.
The Mutation Trade-Off
While highly efficient in terms of memory footprint, populating an array with a single object reference links every element together. Altering a property on any index mutates the shared underlying object:
const list = _.fill(new Array(3), { status: 'pending' });
list[0].status = 'completed';
console.log(list[1].status); // Outputs: 'completed'If individual, independent objects are required, _.fill
is not suitable. In such cases, methods like
Array.from({ length }, () => ({ ... })) or
_.times should be used instead, balancing the trade-off
between independent state and increased memory allocation.