Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the JavaScript heap handle recursion

Hi my question above is a little vague so I'll try to make it clearer.

I have code of the form:

function main () {

  async function recursive () {
     var a = "Hello World";
     var b = "Goodbye World";

     recursive();

  }
  recursive();

}

I am running into an issue where I am running out of heap memory.

Assuming what I showed above is how my program behaves, with a and b being declared within the recursive function, my question is whether the variables are destroyed upon the call to recursive within the recursive function or whether they will persist until there are no more recursive calls and the main function reaches its endpoint assuming I keep the main function running long enough for that to happen.

I'm worried they stay alive in the heap and since my real program stores large strings in those variables I'm worried that that is the reason I'm running out of heap.

like image 773
120394999 Avatar asked Jul 27 '18 23:07

120394999


People also ask

How does JavaScript recursion work?

Recursion is a programming pattern or concept embedded in many programming languages, and JavaScript is not left out. It is a feature used in creating a function that keeps calling itself but with a smaller input every consecutive time until the code's desired result from the start is achieved.

How does heap work in JavaScript?

The heap is a different space for storing data where JavaScript stores objects and functions. Unlike the stack, the engine doesn't allocate a fixed amount of memory for these objects. Instead, more space will be allocated as needed. Allocating memory this way is also called dynamic memory allocation.

Is heap memory used for recursion?

4) Both heap and stack are essential to implement recursion. Heap is not needed for function calls. It is generally used for dynamic memory allocation by user (or programmer).

Does recursion use more memory in JavaScript?

No, recursive function calls do not cause memory leaks in Javascript.


1 Answers

JavaScript does not (yet?) have tail call optimization for recursion, so for sure you'll eventually fill up your call stack. The reason your heap is filling up is because recursive is defined as an async function and never waits for the allocated Promise objects to resolve. This will fill up your heap because the garbage collector doesn't have the chance to collect them.

In short, recursion doesn't use up the heap unless you're allocating things in the heap within your functions.

Expansion of what's happening:

main
  new Promise(() => {
    new Promise(() => {
      new Promise(() => {
        new Promise(() => {
          new Promise(() => { //etc.

You say you're not awaiting Promises because you want things to run in parallel. This is a dangerous approach for a few reasons. Promises should always be awaited in case they throw, and of course you want to make sure that you're allocating predictable amounts of memory usage. If this recursive function means you're getting an unpredictable number of needed tasks, you should consider a task queue pattern:

const tasks = [];

async function recursive() {
  // ...
  tasks.push(workItem);
  // ...
}

async function processTasks() {
  while (tasks.length) {
    workItem = tasks.unshift();

    // Process work item. May provoke calls to `recursive`
    // ...do awaits here for the async parts
  }
}

async function processWork(maxParallelism) {
  const workers = [];
  for (let i = 0; i < maxParallelism; i++) {
    workers.push(processTasks());
  }

  await Promise.all(workers);
}

Finally, in case it needs to be said, async functions don't let you do parallel processing; they just provide a way to more conveniently write Promises and do sequential and/or parallel waits for async events. JavaScript remains a single-threaded execution engine unless you use things like web workers. So using async functions to try to parallelize synchronous tasks won't do anything for you.

like image 149
Jacob Avatar answered Sep 25 '22 20:09

Jacob