Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StackOverflowError during recursive call

Tags:

java

The question is strictly theoretical but I couldn't find an answer. Consider this short program:

public class SBtst {

    int i = 0;

    public static void main(String[] args) {
        new SBtst().rec();
    }

    public void rec() {
        System.out.println("rec is called "+i++);
        try {
            rec();
        } catch (StackOverflowError soe) {
            System.out.println("completed "+soe.getStackTrace().length);
        }
    }

}

After execution I get output similar to:

rec is called 10472
rec is called 10473
rec is called 10474Recursion completed 1024

The questions are:

  1. Why didn't println() completed new line because as I understand it, an error should be thrown when the next rec() call is executed and it is AFTER println() completed

  2. Why do I get only 1024 elements in the stackTrace array but i is equal to 10474? Does it mean that not every call is in the stack trace?

like image 437
skwisgaar Avatar asked Sep 01 '14 17:09

skwisgaar


2 Answers

Why is the exception apparently thrown from within println?

Why didn't println() complete a new line? As I understand, the exception should be thrown when the next rec() call is executed, but that would be after println() has completed.

Your understanding isn't quite correct. At some point the stack looks like:

…
rec()
  rec()
    rec()
      println()

Println consumes resources too (see mattingly890's answer), and there's no reason that you couldn't reach the limit at that point.

Why does the stack trace contain fewer frames than what were actually used?

Why do I get only 1024 elements in the stack trace, even though at least 10474 were used? Does it mean that not every call is in the stack trace?

Have a look at the documentation for getStackTrace():

Provides programmatic access to the stack trace information printed by printStackTrace(). Returns an array of stack trace elements, each representing one stack frame. The zeroth element of the array (assuming the array's length is non-zero) represents the top of the stack, which is the last method invocation in the sequence. Typically, this is the point at which this throwable was created and thrown. The last element of the array (assuming the array's length is non-zero) represents the bottom of the stack, which is the first method invocation in the sequence.

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method. Generally speaking, the array returned by this method will contain one element for every frame that would be printed by printStackTrace. Writes to the returned array do not affect future calls to this method.

In the comments Peter Lawrey found the documentation where the a 1024 default limit is mentioned for stack size:

Java HotSpot VM Options

-XX:ThreadStackSize=512

Thread Stack Size (in Kbytes). (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]

There might be other factors that come into play, too, though. For instance, these questions mention two other options:

  • how to expand size of Java stack trace to see bottom of stack? (triggering a stack overflow) (-XXMaxJavaStackTraceDepth)
  • How to avoid the 1024 lines of Java exception stack limit (-Xss)
like image 111
Joshua Taylor Avatar answered Oct 02 '22 08:10

Joshua Taylor


Part 1:

In the OpenJDK implementation, the println() implementation looks like:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

Thus, the call to print(x) could overflow the stack, and the StackOverflowError would be raised without the newLine() method ever being called.

Part 2:

From the Java API:

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace... Generally speaking, the array returned by this method will contain one element for every frame that would be printed by printStackTrace.

Thus, the JVM is free to impose a limit, such as 1024, on the number of stack frames stored in the stack trace.

like image 40
therealrootuser Avatar answered Oct 02 '22 08:10

therealrootuser