0

Imagine I have this hypothetical function (in javascript):

function paginationRecursive(results) {
  let intermediate = retrieveNextPage();
  results = results.concat(intermediate)
  if (haveAllResults()) return results;
  return paginationRecursive(results);
}

Will each value of intermediate be kept in memory until the whole recursive processing has completed, thus everytime increasing memory usage for every recursive call? Or is the engine/gc smart enough to free this memory at any time we go one call "deeper", because it knows that the variable will never be used again?

It could be that it is engine specific (e.g. V8, SpiderMonkey, Chakra, etc.) so in order to make this question not to broad, I prefer to know the answer for the V8 engine.

Titulum
  • 9,928
  • 11
  • 41
  • 79
  • Judging from your title, do you *have* an actual memory problem in your real code? Btw, it's not a leak since surely the memory gets cleaned up after the function ends. – Bergi Jun 16 '19 at 19:47
  • I don't have a memory problem, but I'm thinking about implementing an function using this approach. I know the memory would be freed when the function ends, but will it free the memory when processing goes one (or many) calls deeper, before the actual function end? – Titulum Jun 16 '19 at 20:17
  • I don't think it really matters. The stack frame will be kept, yes, since V8 still has not implemented tail calls properly, but the memory allocated for the `intermediate` variable will be negligible. – Bergi Jun 16 '19 at 20:26
  • Hmmm, you can not assume that. We don't know the size of the data contained in the `intermediate` variable. It could be 500mb every time for all we know. But what you said about V8 not properly implementing tail calls is what I'm interested in. So the GC will not free all previous `intermediate` values if it needs more memory? – Titulum Jun 16 '19 at 20:51
  • The size of the data contained in the variable doesn't matter, since the data is also contained in the `results` array which has to be kept in memory anyway. – Bergi Jun 16 '19 at 21:06
  • That is indeed true, but will the GC free the memory for the `intermediate` variables from precious calls in the stack before eventually running out of memory is the real question. – Titulum Jun 16 '19 at 21:10
  • 1
    I'd strongly suggest mentioning "tail-calls" or "tail-recursion" in the question. This could probably be marked as a dup of https://stackoverflow.com/questions/37224520/are-functions-in-javascript-tail-call-optimized – Rob Starling Jun 16 '19 at 22:50

1 Answers1

1

I think as of right now, the answer is "the intermediate variable should be released, but it won't be".

ES6 does talk about tail-position calls and even requires optimization:

A tail position call must either release any transient internal resources associated with the currently executing function execution context before invoking the target function or reuse those resources in support of the target function.

A decent overview of TCO (tail-call optimization) in JS is http://2ality.com/2015/06/tail-call-optimization.html

That all being said, that page links to this table showing that pretty much no runtimes actually support it: https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)

This r/node reddit thread offers some insight and links to this v8 blog post which includes:

[...] the V8 team strongly support denoting proper tail calls by special syntax. There is a pending TC39 proposal called syntactic tail calls to specify this behavior, co-championed by committee members from Mozilla and Microsoft. [...] The V8 team plans to resolve the issue at the next TC39 meeting before shipping implicit proper tail calls or syntactic tail calls by default.

which is to say, V8 doesn't want to implement that ES6 feature as-spec'd and would prefer an explicit syntax for tail-calls, like the ones proposed for TC39

See also: Are functions in JavaScript tail-call optimized?

Rob Starling
  • 3,868
  • 3
  • 23
  • 40