JavaScript Stack vs Heap Memory Allocation

JavaScript engines manage memory using two primary data structures: the stack and the heap. This article explains how primitive values, reference types, and function execution contexts are allocated between these two memory spaces, detailing the rules JavaScript engines use to handle dynamic data, pointers, and memory management.

The Memory Stack: Fast, Structured, and Static

The stack is a contiguous block of memory that operates on a strict Last-In, First-Out (LIFO) order. It is managed directly by the CPU and handles static memory allocation for active execution contexts.

Stack allocation and deallocation are extremely fast because the engine only needs to move the stack pointer up or down. When a function finishes executing, its entire stack frame is removed automatically.

The Memory Heap: Dynamic and Unstructured

The heap is a large, unstructured pool of memory dedicated to dynamic memory allocation. Unlike the stack, memory in the heap is not allocated or freed in a predetermined order.

How Allocation Works in Practice

Consider the following assignment:

function createUser() {
  const age = 30;
  const user = { name: "Alice" };
  return user;
}

const newUser = createUser();
  1. Stack Allocation for Primitives: The primitive value age (30) is stored directly in the stack frame of createUser.
  2. Heap Allocation for Objects: The engine creates the object { name: "Alice" } inside the heap.
  3. Stack Pointer Assignment: The local variable user on the stack receives the memory address pointing to the object on the heap.
  4. Function Exit: When createUser finishes, its stack frame is popped, destroying the local age variable. However, because the object reference is returned and assigned to newUser, the heap memory for { name: "Alice" } remains allocated and accessible.

Engine Optimizations and Special Cases

Modern JavaScript engines (like Google’s V8, SpiderMonkey, and JavaScriptCore) apply internal optimizations that can alter standard allocation rules: