Understanding the Java stack
Asked Answered
M

7

26

There is this code:

public class Main {
    public static void main(final String[] args) throws Exception {
        System.out.print("1");
        doAnything();
        System.out.println("2");
    }

    private static void doAnything() {
        try {
            doAnything();
        } catch (final Error e) {
            System.out.print("y");
        }
    }
}

And there is the output:

1yyyyyyyy2

Why does it print "y" eight times and no more. How can Java call println() when StackOverflowError encountered?

Minsk answered 26/2, 2013 at 7:7 Comment(11)
1yyyyyyyy2 is wrong. it outputs 1y2.the answer is straight forward actually. you are catching the error and suppressing StackOverflow by printing a message.Debarath
Seems to be a situation similar to this question except that is try...finally and this is try...catch.Ale
it prints 1yyyyyyyyy2 on my systemKarykaryl
@PremGen It's random, try for couple of times, the number of y's dwindle.Cabernet
ideone.com/TIpuuwNicaea
Another view to the problem: ideone.com/XRm9kj You see that even I used println there is no newline printed!Ale
As of my understanding it should only print once, since the error, occurs only at the last call, then it will unwind the stack without error, omitting the print code???Stearne
Could it be due to the stack trace containing more and more things that fills up the stack?Ale
So on my machine (jdk7 Windows x64) I was unable to get multiple ys, same as @PremGenError and I cannot debug it because it crashes.Ale
Try out this code before write that is wrong. On my system exactly 8 times prints "y", other computer other times. @Cong Xu link reproduce that: ideone.com/TIpuuwMinsk
See also this code: It prints only 15 y but the catch block is executed (at least) 29 times.Ale
C
13

Here you are catching Error and not Exception in which case your program would have crashed.

If you try this code (modified to add a static counter)

public class StackError {

static int i = 1;

public static void main(final String[] args) throws Exception {
    System.out.print("1");
    doAnything();
    System.out.println("2");
}

private static void doAnything() {
    try {
        i++;
//          System.out.println(i);
        doAnything();
    } catch (Error e) {
        System.out.print("y"+i+"-");

    }
}
}

Output

 1y6869-2

So, it has got stackerror 6869 times(changes for different runs) and the last value is printed. If you just print the y as you did earlier then it might the case that the output is getting bufferred and not getting flushed as it is not a println.


Update

The System.out.println internally calls the PrintStream which is buffered. You don't loose any data from the buffer, it gets all written to the output( terminal in your case) after it fills up, or when you explicitly call flush on it.

Coming back to this scenario, it depends on the internal dynamics of how much the stack is filled up and how many print statements were able to get executed from the catch in doAnything() and those number of characters were written to the buffer. In the main back it finnally get's printed with the number 2.

javadoc reference to buffered streams

Cabernet answered 26/2, 2013 at 7:23 Comment(3)
Yes, I give same result with counter. But without counter my code prints 8 times "y".Minsk
Now also see this code: ideone.com/Lqoaod The counter is incremented 29 times.Ale
Why? I understand the PrintStream buffering, but why go to the catch block 29 times?Minsk
M
5

My bet is that by invoking print in the catch block you force another StackOverflowError that is caught by the outer block. Some of these calls will not have enough stack for actually writing the output stream.

The JLS says that:

Note that StackOverflowError, may be thrown synchronously by method invocation as well as asynchronously due to native method execution or Java virtual machine resource limitations.

The Java SE platform permits a small but bounded amount of execution to occur before an asynchronous exception is thrown.

The delay noted above is permitted to allow optimized code to detect and throw these exceptions at points where it is practical to handle them while obeying the semantics of the Java programming language. A simple implementation might poll for asynchronous exceptions at the point of each control transfer instruction. Since a program has a finite size, this provides a bound on the total delay in detecting an asynchronous exception.

Mattland answered 26/2, 2013 at 7:32 Comment(1)
Ah, "small but bounded", as opposed to "small but unbounded". :DOconner
D
3

The first time the StackOverFlowError occurs, the call to the last doAnything() is cancelled and the control is returned to the catch block from the last doAnything().

However, because the stack is still practically full, the simple fact of calling System.out.print("y") will causes another StackOverflowError because of the need of pushing some value on the stack and then make a call to the function print().

Therefore, another StackOverflowError occurs again and the return is now returned on the catch{} block of the previous doAnything(); where another StackOverflowError will happens because the need of stack space required to do a single call to System.out.println("y") is greater than the amount of space liberated from returning a call from doAnything().

Only when there will be enough space on the stack to execute a call to System.out.print("y") that this process will stop and a catch block will successfully complete. We can see that by running the following piece of code:

public class Principal3b {

    static int a = 0;
    static int i = 0;
    static int j = 0;

    public static void main(String[] args) {
      System.out.println("X");
        doAnything();
      System.out.println("Y");
        System.out.println(i);        
        System.out.println(j);
    }

    private static void doAnything() {

        a++;
        int b = a;

        try {
            doAnything();
        } catch (final Error e) {
            i++;
            System.out.println(a);
            System.out.println(b);
            j++;
        }
    }
}

Notice that a println(a) is used instead of a print(a); therefore a new line should be printed after each value of a if everything runs OK.

However, when I run it, I get the following result:

X
62066206620662066206620662066206
6190
Y
17
1

This means that there have been 17 attempts ro run the catch block. Of these catch block executions, 9 are unable to print anything before generating themselves a StackOverflowError; 7 are able to print the value of 6190 but are unable to print a newline after it before themselves rising an error again and finally, there is one that is able to both print the value of 6190 and the newline after it; therefore finally permitting its catch block to complete without any new StackOverflowError and return gracefully up the calls stack.

As we are dealing with StackOverflowError, these numbers are only an example and will vary greatly not only between machines but also between executions and the simple fact of adding or removing any kind of instructions should also change these values. However, the pattern seen here should remains the same.

Dupuy answered 27/2, 2013 at 8:33 Comment(1)
+1 IMO this is the answer that actually answers the OP's question as to why the catch block is executed multiple timesHollinger
L
1

One thing is clear that System.out.print("y"); in catch creates this puzzle. If we change the code as

static int n;

public static void main(final String[] args) throws Exception {
    System.out.println("1");
    doAnything();
    System.out.println(n);
}

private static void doAnything() {
    try {
        doAnything();
    } catch (Error e) {
        n++;
    }
}

it prints

1
1
Larvicide answered 26/2, 2013 at 7:47 Comment(0)
K
0

Well the no. of times the stack overflow error is hit is undefined. However, the JVM allows you to recover from StackOverflowError error and continue execution of the system normally.

It is proved by the following code:

public class Really {
   public static void main(final String[] args) throws Exception {
     System.out.print("1");
     doAnything();
     System.out.println("2");
   }
   private static void doAnything() {
    try {
           throw new StackOverflowError();
       //doAnything();
    }
    catch(final Error e){
        System.out.print("y");
    }
   }
}

Note however, as @Javier said, the StackOverflowError is thrown by the JVM synchronously or asynchronously(which means it can be thrown by another thread, possibly a native thread) which is why it is not possible to get the stack trace of the error. The no. of times the thread(s) hit the catch() block is undefined.

Karykaryl answered 26/2, 2013 at 7:40 Comment(3)
So you're manually throwing a StackOverflowError but in this case the stack isn't even "almost full" so that's different from the actual case.Ale
@AlvinWong, the program is to prove that the JVM is allowed to continue executing the program even after an "Error" has been caught. Even an error as severe as "StackOverflowError"Karykaryl
either you throw it or the JVM's monitor thread.. does not matterKarykaryl
P
0

In addition, Objects of type Error are not Exceptions objects they represent exceptional conditions. Errors represent unusual situations that are not caused by program errors, usually does not normally happen during program execution, such as JVM running out of memory. Although they share a common superclass Throwable, meaning both can be thrown, it can be placed in a catch but generally not supposed to be caught, as they represent rare, difficult-to-handle exceptional conditions.

Promisee answered 1/3, 2013 at 7:33 Comment(0)
S
-2

Stack overflow.

You are only printing on exception,in the meantime the program recurses into overflow.

At which point this occurs depends on individual systems, memory, etc.

What is the purpose of the program?

Sumer answered 26/2, 2013 at 7:11 Comment(2)
You keep recursing until you hit an exception, and only then start printing. The state of the JVM and program at this point is undefined.Sumer
I know, this code will cause a stack overflow. But the question was why print eight times "y" instead of exactly one time? The puspose of the program is on the title :)Minsk

© 2022 - 2024 — McMap. All rights reserved.