9

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?

James M
  • 18,506
  • 3
  • 48
  • 56
skwisgaar
  • 880
  • 2
  • 13
  • 31
  • 4
    Where does the word "Recursion" get printed? I suspect the program doesn't match the output. – Peter Lawrey Sep 01 '14 at 17:05
  • The stack trace has a limit of 1024 levels. – Peter Lawrey Sep 01 '14 at 17:05
  • where is your base case? – Kick Buttowski Sep 01 '14 at 17:06
  • 1
    @PeterLawrey Do you have a source for that claim? The docs don't seem to mention it, though they do allow the trace to omit frames… – Joshua Taylor Sep 01 '14 at 17:08
  • 2
    @JoshuaTaylor See http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html under `-XX:ThreadStackSize=` where it says the default is `1024` on many systems. – Peter Lawrey Sep 01 '14 at 17:11
  • 1
    @PeterLawrey So it's not an absolute limit, but 1024 is the default. Thanks! – Joshua Taylor Sep 01 '14 at 17:15
  • 1
    @JoshuaTaylor what would be smarter is if it could remove duplicates esp if they are just after each other. – Peter Lawrey Sep 01 '14 at 17:18
  • @PeterLawrey Maybe, but that way probably lies madness. What if you've got mutually recursive methods and you expect to see `a a b b a a b b` and that gets collapsed to `a b a b`? It's a nice thought, but I expect that just about "optimization" here would fail pretty nastily for some cases. – Joshua Taylor Sep 01 '14 at 17:23
  • 1
    Should this go to Meta.SO, since this is _about_ StackOverflow? :P – sampathsris Sep 02 '14 at 04:16
  • @PeterLawrey, correct, forgot to remove 'Recursion' from output, there were a few more lines in rec() which I deleted as irrelevant to the questions – skwisgaar Sep 02 '14 at 11:11

2 Answers2

8

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:

Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • the op does not have base case at all or I do not see it? – Kick Buttowski Sep 01 '14 at 17:07
  • 3
    That's why it's a "strictly theoretical" question. – therealrootuser Sep 01 '14 at 17:07
  • 1
    @KickButtowski OP doesn't need a base case to ask this question. It's not about why the recursion doesn't terminate, it's about why the exception is thrown from an unexpected place, and why the number of frames in the trace isn't the same as the depth of the recursion. – Joshua Taylor Sep 01 '14 at 17:09
8

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.

therealrootuser
  • 10,215
  • 7
  • 31
  • 46