JavaScript Memory Model for Beginners
Understand the stack and heap in JavaScript. Learn where primitives and objects are stored, how garbage collection works, and why this matters for your code.
The JavaScript memory model most engines follow uses two memory regions: the stack for fast, short-lived data, and the heap for larger, longer-lived data. JavaScript manages memory for you automatically, but understanding where and how your data is stored helps you write more efficient code and avoid subtle bugs.
Every time you declare a variable, JavaScript decides where to put its value based on the type. Primitives go on the stack. Objects go on the heap with a pointer on the stack.
This split explains why primitives and objects behave so differently during assignment and comparison.
The ECMAScript specification never mandates a stack or a heap. It only defines behavior, such as primitives being compared by value and objects by reference. "Stack for primitives, heap for objects" is the mental model most engines, including V8, use to deliver that behavior, and it is a reliable way to reason about your code. Just do not treat it as a rule written in the spec itself.
The stack holds actual primitive values and references to objects. The heap holds the objects themselves. When you assign an object to a variable, the variable stores a memory address that points to the heap, not the object data itself.
The Stack: Fast and Temporary
The stack is a region of memory that follows a last-in, first-out structure. It is fast to allocate and deallocate because the engine only needs to move a pointer to add or remove data from the top.
Primitive values like strings, numbers, and booleans are stored directly on the stack. Each variable gets its own memory slot with its own copy of the value:
let color = "blue";
let count = 42;
let active = true;Each of these variables occupies a small, fixed-size slot on the stack. The variable color holds the actual string "blue", not a pointer to somewhere else. This direct storage is what makes primitives fast to access and safe to copy.
When a function is called, its local variables are pushed onto the stack. When the function returns, the entire stack frame is popped off and the memory is reclaimed automatically. This is why local variables disappear when a function finishes:
function calculate() {
let result = 100; // pushed onto the stack
return result; // popped off after return
}The stack is perfect for temporary data with a known, fixed size. But it is not suitable for objects, which can grow and shrink dynamically during the program's lifetime.
The Heap: Flexible and Shared
The heap is a larger, more flexible region of memory for data whose size cannot be determined at compile time. Objects, arrays, functions, and other complex data structures live on the heap.
When you create an object, JavaScript allocates memory for it on the heap. The variable on the stack does not hold the object itself. It holds a reference, which is essentially the memory address where the object lives:
let user = { name: "Alex", age: 30 };The variable user sits on the stack. It contains a memory address that points to the actual object data on the heap. When you access user.name, JavaScript follows the reference from the stack to the heap and retrieves the name property from the object.
This reference-based storage explains why assigning an object to a new variable does not create a copy. Both variables end up with the same memory address, pointing to the same heap object:
let original = { value: 10 };
let reference = original;
reference.value = 20;
console.log(original.value); // 20 (same object, changed through reference)Modifying the object through one variable affects what you see through the other. They both point to the same heap allocation.
Garbage Collection
JavaScript automatically frees heap memory that is no longer reachable through a process called garbage collection. You do not need to manually deallocate memory.
An object becomes eligible for garbage collection when no variable on the stack points to it anymore. The most common way this happens is when a variable goes out of scope or is reassigned:
function createUser() {
let user = { name: "Alex" }; // object allocated on heap
// user goes out of scope when function returns
// object becomes unreachable, eligible for collection
}
createUser();After the function returns, the local variable user is popped off the stack. The object on the heap now has zero references pointing to it. The garbage collector will eventually find it and free the memory.
You can help the garbage collector by nullifying references to large objects when you are done with them:
let bigData = loadLargeDataset(); // large object on heap
processData(bigData);
bigData = null; // remove reference, allows collectionSetting the variable to null breaks the reference from the stack to the heap. If no other references exist, the object becomes unreachable and will be garbage collected. This is a best practice for memory-intensive applications, though for most everyday code, the automatic collection works fine without manual nullification.
Why the Model Matters
The stack and heap distinction explains three behaviors you encounter every day in JavaScript.
First, primitives are copied by value. When you assign a primitive to a new variable, JavaScript copies the actual value from one stack slot to another. The two variables are independent.
Second, objects are copied by reference. When you assign an object to a new variable, JavaScript copies the memory address, not the object. Both variables point to the same heap data.
Third, const prevents reassigning the stack slot but does not lock the heap data. A const object variable cannot point to a different object, but the object itself can still be modified:
const config = { theme: "dark" };
config.theme = "light"; // allowed: heap data changes
// config = { theme: "blue" }; // TypeError: stack pointer cannot changeFor a deeper look at how primitives are stored, see the guide on how JavaScript stores primitive values. For the behavioral difference between types, read the primitive vs reference types guide.
Rune AI
Key Insights
- The stack stores primitives and references; the heap stores objects.
- Primitives are immutable and stored directly in the variable's memory slot.
- Objects live on the heap; variables hold pointers to them.
- Garbage collection automatically frees unreachable heap memory.
- Understanding the memory model explains copy-by-value vs copy-by-reference.
Frequently Asked Questions
Does JavaScript have automatic memory management?
Can I force garbage collection in JavaScript?
Where are function calls stored?
Conclusion
JavaScript uses two memory regions: the stack for primitive values and object references, and the heap for the actual object data. Primitives are copied by value. Objects are shared by reference through stack pointers. Garbage collection automatically frees heap memory that is no longer reachable, so you rarely need to think about memory management directly.
More in this topic
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.
How to Loop Through Arrays Using JS for Loops Guide
Looping through arrays is the most practical use of JavaScript for loops. Learn how to access each element by index, avoid off-by-one errors, and choose between for, for...of, and forEach.