Maximum Call Stack Size in JavaScript Engines
This article examines the maximum call stack size across major JavaScript engines, explaining how stack limits work, how they vary across environments like Chrome (V8), Firefox (SpiderMonkey), and Safari (JavaScriptCore), and the primary factors that determine when a “RangeError: Maximum call stack size exceeded” occurs.
ECMAScript Specification and Call Stack Limits
The ECMAScript specification does not define a fixed maximum call stack size. Instead, stack limits are an implementation detail determined by individual JavaScript engines, host operating systems, and available memory.
When a function is called, the engine allocates a stack frame
containing arguments, local variables, and return addresses. If
recursion or nested function calls exceed the allocated stack memory
buffer, the engine terminates execution and throws a
RangeError.
Typical Limits Across Major Engines
Stack size limits are generally measured either in bytes of memory allocated to the stack or in the total number of nested function call frames.
- V8 (Google Chrome, Node.js): Typically allows
between 10,000 and 25,000 calls for simple functions. In Node.js, the
default stack size is usually around 984 KB to 1 MB, configurable via
the
--stack-sizeflag. - SpiderMonkey (Mozilla Firefox): Often supports significantly higher frame counts, frequently reaching between 50,000 and 500,000 recursive calls depending on frame complexity and available memory allocation.
- JavaScriptCore (Apple Safari): Generally ranges between 35,000 and 65,000 calls on desktop systems, with smaller limits on iOS devices due to tighter memory constraints.
Factors Influencing Stack Size
The maximum number of recursive calls is not a constant value. It depends on several key variables:
- Stack Frame Complexity: Functions with many local variables, arguments, or large scopes consume more memory per frame, reducing the total number of calls before reaching the stack limit.
- Environment and Architecture: 64-bit architectures require larger pointers than 32-bit systems, increasing the size of each stack frame and lowering the total frame count.
- Tail Call Optimization (TCO): Although specified in ES6, only JavaScriptCore (Safari) consistently implements Proper Tail Calls (PTC). In engines with TCO support, recursive calls in the tail position reuse the current stack frame, theoretically allowing infinite recursive depth.
Measuring Call Stack Size
The exact limit of an environment can be tested using a simple recursive counter:
let depth = 0;
function measureStack() {
depth++;
measureStack();
}
try {
measureStack();
} catch (error) {
console.log(`Maximum stack depth: ${depth}`);
console.error(error.message);
}How to Prevent Stack Overflow Errors
When dealing with deep recursion, use the following strategies to prevent exceeding the call stack limit:
- Convert Recursion to Iteration: Refactor recursive
logic into iterative
whileorforloops using an explicit array as an in-memory stack stored on the heap. - Trampolining: Wrap recursive calls in thunk functions and resolve them iteratively, keeping the active call stack at a depth of one.
- Asynchronous Scheduling: Defer execution using
queueMicrotask(),Promise.resolve(), orsetTimeout()to clear the call stack between operations.