Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to estimate javascript call stack maximum size?

I have 2 node js programs.

console.log('TEST A:');
function computeMaxCallStackSizeA() {
    try {
        return 1 + computeMaxCallStackSizeA();
    } catch (e) {
        return 1;
    }
}

for (let i = 0; i < 5; i++)
  console.log(computeMaxCallStackSizeA());
  
console.log('\nTEST B:');

function computeMaxCallStackSizeB() {
    try {
        let a = [];
        for(let i=0;i<100;i++) a.push('1234567890');
        return 1 + computeMaxCallStackSizeB();
    } catch (e) {
        return 1;
    }
}

for (let i = 0; i < 5; i++)
  console.log(computeMaxCallStackSizeB());
  • The first program's result is 12559.
  • The second program's result is 13870.
  • Why does the second program use more memory but still has a larger call stack maximum size?

UPDATE:

  • If we change the program as follow:

function computeMaxCallStackSizeC() {
    try {
        let a1 = '11111111111111111111111111111111111111111111111111111';
        let a2 = '22222222222222222222222222222222222222222222222222222';
        let a3 = '33333333333333333333333333333333333333333333333333333';
        let a4 = '44444444444444444444444444444444444444444444444444444';
        let a5 = '55555555555555555555555555555555555555555555555555555';
        let a6 = '66666666666666666666666666666666666666666666666666666';
        return 1 + computeMaxCallStackSizeC();
    } catch (e) {
        return 1;
    }
}

for (let i = 0; i < 5; i++)
    console.log(computeMaxCallStackSizeC());

The maximum recursion depth is decreased. Because Stack memory is used to store local variables (such as a number, a string) and heap memory is used to store the data of dynamically allocated pointer (such as an array). Is that right?

like image 909
do minh Thang Avatar asked Jul 15 '20 18:07

do minh Thang


People also ask

What is the maximum call stack size in JavaScript?

The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified.

How do you calculate maximum call stack size exceeded?

Sometimes you get Maximum call stack size exceeded error if you accidentally import/embed the same JavaScript file twice. So its worth checking in your resources tab of the inspector to solve this issue.

How do I fix maximum call stack size exceeded see JavaScript console for details?

The "RangeError: Maximum call stack size exceeded" error occurs when a function is called so many times that the invocations exceed the call stack limit. To solve the error, specify a base case that has to be met to exit the recursion.

What is the maximum size of stack?

In Microsoft Windows 2000, if the Microsoft ASP.NET Worker Process (ASPNet_wp.exe) creates a thread, the maximum stack size of the thread is 1 MB. In Windows Server 2008 and higher, the maximum stack size of a thread running on 32-bit version of IIS is 256 KB, and on an x64 server is 512 KB.


Video Answer


1 Answers

My guess would be that the function gets optimised earlier in the second program than in the first because it does more work, and that the optimised function needs less stack space. -- @Bergi

Correct. The presence of the loop means that the function does more work per invocation, and that causes it to get optimized after fewer invocations. Optimizing a function generally results in it using a different stack frame size. I don't think there's a general rule about the relative sizes of these stack frames; apparently in this case the optimized version of the function uses a smaller stack frame, and I assume that's the vastly more common case, but there might well be counter-examples where it's the other way round.

Once the first function (A) gets optimized, since it does less stuff it also uses less stack space than B, so reaches a higher maximum recursion depth.

With JavaScript being as dynamic as it is and modern engines collecting type feedback and using that for decision-making during compilation, you may also well see the same function take different amounts of stack space on different times that it gets optimized -- e.g. when you use a library/helper function in two different apps, or when you call it with other inputs, etc.

Also, regardless of optimized compilation, stack frame sizes (and hence maximum recursion depths) can and do change depending on:

  • which hardware you're running on (since machine code is, by definition, specific to a given CPU architecture),
  • which engine you're using (V8/SpiderMonkey/JavaScriptCore/etc...),
  • which version of that engine you're using (as its developers make changes to such internal details of how it operates),
  • potentially which embedder (say, Chrome or Node) you're using, even if they use the exact same version of the same engine,
  • and potentially which operating system you're running on.

So it is not advisable to rely on a particular value.

Why does the second program use more memory but still has a larger call stack maximum size?

The array is allocated on the heap, so the size of the array doesn't affect the function's stack frame size. Observe how if you replace "100" with "10", the result is exactly the same.

It's hard to give an accurate yet intuitive idea of what consumes stack space. It boils down to "local values that have to be kept around", which sometimes corresponds to local variables, unless the compiler can get away with not allocating a stack slot for a local variable (and keeping its value in a register instead). Also, often there are additional "internally used" slots to hold temporary values that you don't directly see in the JavaScript code, especially for more complicated functions, where e.g. an object property might be read only once, but is used twice, and in the meantime is stored in a stack slot. Or when growing an array (as a.push(...) does), which is a fairly complex operation under the hood and as such causes a bunch of temporary values to be stored on the stack.

How to estimate javascript call stack maximum size?

Under the hood, at least in V8, the limit is a little less than a megabyte. In terms of JavaScript function calls, a decent rule of thumb is:

"A few hundred calls, or a few thousand if your functions are tiny."

like image 188
jmrk Avatar answered Sep 17 '22 18:09

jmrk