简体   繁体   中英

Recursion runtime implementation Java vs. other/functionals languages?

I like recursion, but at Java you meet an dead end at some point. Eg I had a case where recursion with ~100K iterations wouldn't work (StackOverflowError). Badly I had to switch to annoying "imperative looping" for this runtime stack-limit reasons.

I wonder how other (especially functional) languages bypass stack-overflowing during runtime? I guess especially functional language runtimes handle this problem better because recursion is core-concept...

Has somebody some information or external resources?

Most languages have compiler optimizations for tail recursion . Tail recursion means that the recursive call should be the last call of your recursive method. The compiler can then optimize this into a loop, preventing stack overflow errors from happening.

I'm not sure whether all javac implementations implement tail recursion. It is not something that is required by the specification. However, it's an important optimization technique for any recursive program, so I suppose the mainstream implementations do provide tail recursion.

You can test this yourself by taking a (simple) non-tail-recursive program that generates a StackOverflowError and make it tail-recursive (calculating a factorial , for example).

EDIT : There has been a question about tail recursion in Java before, as indicated in a comment by user sje397. Also take a look at Stephen C's answer to this question, which provides some additional info.

This is a followup to @Ronald Wildenberg's answer:

I'm not sure whether all javac implementations implement tail recursion. It is not something that is required by the specification.

The short answer is that they don't support it.

The longer answer is that tail recursion optimization is a tricky problem in Java because of the JVM design. Read this blog entry by John Rose @ Oracle where he talks about this problem. The main thrust of the blog entry is a proposal for a bytecode extension to support "hard" tail calls. But the last paragraph hints as why implementing "soft" (ie transparent) tail calls is hard. Tail call optimization interferes with the JVM's ability to capture a stack trace, and that has "implications" for the Java security architecture.

This Sun Bug Database Entry gives more details on the problem. Read the comments as well.

It seems that the way to implement tail recursion on the current JVM is to implement it in the compiler front-end. Apparently Scala does this.

"recursion with ~100K iterations" is something that should be avoided, not just in Java. It works only due to tail call optimization, which is not always possible. So it's better not to get into the habit of excessive recursion in the first place.

Recursion is one of those concepts people tend to overuse when they first learn them, because it seems just too cool not to show off everywhere...

As other people have mentioned, support for proper tail recursion helps. But by itself it's not enough to support deep recursion. And despite what BLUB programmers might think, deep recursion is a natural fit for some tasks, such as processing deeply recursive data structures.

Strategies for supporting deep (non-tail) recursion are often subsumed by strategies for supporting first-class continuations. You might find Implementation Strategies for First-Class Continuations (Clinger et al) interesting.

A few strategies off the top of my head:

  • CPS-convert your program or otherwise heap-allocate continuation frames (disadvantage: loses some performance advantages of the stack)
  • on stack overflow, copy the stack out to a separate block of memory and reset the stack pointer to the base; on underflow, copy the old stack back in
  • on stack overflow, allocate a new stack region from the heap and reset the stack pointer there; on underflow, go back to the end of the old stack
  • on stack overflow, create a new thread to continue running the computation, and wait for the thread's result (disadvantages: creating a thread may be expensive (but worker threads can be cached), and moving a computation across threads may cause havok with thread-local storage etc)

You can increase java stack size with:

java -Xss1024k MyProgram

(will increase the stack size of java upto 1024 KB.) But generally it is not a good idea to use that deep recursion. Try to make iterative solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM