3

I came across this program from a book called Java Puzzlers. The book explains its behaviour, but I was not able to get everything.

The book says the following program terminates in 1.7 × 10291 years, assuming a computer which is processing it. It also says it throws StackOverflowError 21,024 times, but it still runs.

public class Workout {

    public static void main(String[] args) {
        workHard();
        System.out.println("It’s nap time.");
    }

    private static void workHard() {
        try {
            workHard();
        } finally {
            workHard();
        }
    }
}

I want to understand where the extra stack is coming from and will the program affect other programs running of machine consuming their stack?

The technical explanation is in the book. What is the explanation in layman's terms, like very feynmanly?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abhinav Chauhan
  • 1,304
  • 1
  • 7
  • 24
  • answer so far are very helpful , thank you all , can you please add some visual clue too so that i and future readers can understand very easily – Abhinav Chauhan Mar 28 '20 at 16:43

3 Answers3

2

Let's index the bottom of stack to 0 and subsequent calls (since it is recursive) at stack level 1,2,3 and so on. Each call to workHard will increase the stack index by 1 and each return should decrease the stack length by 1.

Since there isn't any return statement, the piece of code forms an infinite recursion and the only way to come out of this recursion loop is a stack overflow exception that breaks this cycle.

Consider the modified piece of code without try/finally:

public class Workout {

    public static void main(String[] args) {
        workHard();
        System.out.println("It’s nap time.");
    }

    private static void workHard() {
        workHard();
    }
}

In the above piece of code, assuming that you got the exception at n'th index of stack. The exception is thrown to n-1th stack which doesn't have any way to handle the exception, hence the exception is thrown to n-2nd stack level and up to 0th level. After getting the exception at the 0th level, a exception is thrown to the main method which too doesn't have any mechanism to handle this exception and an exception is thrown.

Now coming to the code with the try block:

public class Workout {

    public static void main(String[] args) {
        workHard();
        System.out.println("It’s nap time.");
    }

    private static void workHard() {
        try {
            workHard();
        } finally {
            workHard();
        }
    }
}

Again, at the beginning a recursive call will be first made from the try block and keep on making calls till the n'th level where we get our first stack overflow exception while making a recursive call for the n+1th stack level.

For each stack overflow exception coming from try block, the next line of execution will be the call to workHard in the finally block. At the peak of the stack overflow exception at the n'th level, this exception would be caught and execution goes to the finally block of the n'th level.

Since we already have very limited stack size, this finally block will also throw a stack overflow exception and the exception would be caught at the n-1th stack level. Now see that we have freed one stack level. Now the call in the finally block would again be a success and go till the n'th level before it could throw a stackoverflow exception.

Now we get the stack overflowexception at the n'th stack level. Execution goes to the finally block which again throws an exception and this exception is received in the finally block at the n-1th level. Since there isn't any way to handle the exception, the exception is thrown to the n-2th stack level, which catches the exception and retrigger the recursion.

Note that we have freed two stack levels by now.

Now the recursion level goes till the n'th level again and control returns back to n-3th and reaching again till n'th and returning back to n-4th and so on.

At some time, we will reach the finally block of the 0th stack level and again will go to the n'th level through try and then the finally block before finally throwing the exception to the main method.

The stack is being freed and occupied again and again and hence these many overflow exceptions and time to terminate.

Coming to the second part, will it affect other programs?

No, it shouldn't. The stack size runs out of space out of what was allocated to the JVM. The OS will only allocate limited stack size to the JVM. Which can be also controlled via JVM arguments if required.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SJ1
  • 344
  • 3
  • 12
0

It is a recursive method call.

There is a limitation for stack calls, so when the number of calls reach the limitation in a try block it throws a stack overflow exception.

After the exception, it tries to run the code in the final block. But due to the limitation, it raises another exception that causes to exit form the nested method call and makes a free space for another method call.

This scenario is repeated in the outer level again and again. Once it gets a free space in the stack it attempts to fill it by another method call. This is why it continues up to there is no memory.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3359139
  • 430
  • 4
  • 17
  • that is what i already know , i want to know if stack is once overflowed where the stack comes for more calls , and once the finally throws exception why doesn't it terminate. – Abhinav Chauhan Mar 28 '20 at 16:12
  • I edited the answers, But notice StackOverflow is a typical exception that you can handle ti or force to execute a final block before it exits from the function. – user3359139 Mar 28 '20 at 16:17
  • Please note that StackOverflow doesn't mean termination or "not enough memory". it just says the limitation is reached so you should handle it. – user3359139 Mar 28 '20 at 16:19
  • let me explain in other words, Yes when StackOverflow happens it says you don't allow to call another method but if there is a final block it executes. Since there is still a forbidden call condition while you call a method in the final block it throws exception again. and go out to the other method call. At this time reveals a space in the stack for another method call. – user3359139 Mar 28 '20 at 16:36
0

You are basically using recursion. And the recursion does not have any termination condition. So if you run this code, it will not terminate until a memory error occurs.

Now to come to the point, how does a memory error occur here?

When a program execution starts, the program runs in a thread (if the program doesn't have multi-thread set up). Stack memory is allocated for the current thread.

When a function makes a recursive call,

  • The current function pauses its execution
  • The current function stores the environment (local variables, where the control is now, some internal details, etc. known as the execution context) in the current thread stack, known as the context stack
  • A nested function call starts its execution from the current function
  • When the nested function execution completes, the calling function resumes its execution from the interruption point.

So, for every recursive call, an execution context is getting stored in the context stack. And if the recursion does not contain any termination condition or if the memory fills before the recursion reaches to the termination condition, you will get the StackOverFlowError, since the stack memory gets filled for current executing thread.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
S.Mandal
  • 172
  • 1
  • 10
  • 1
    i thought there is only one stack, where all the method calls stores that is why it get overflowed, if i am not wrong are you're saying every call creates a stack, so stack should not overflow, the error should be something else , but it throws stackoverflow error as i mentioned in the question – Abhinav Chauhan Mar 28 '20 at 16:32
  • Java uses one single Stack per Thread; that's where it will put the call context. – tquadrat Mar 28 '20 at 16:35
  • Sorry, my bad. One stack is used per thread. And it is shared for all function call. I have updated my answer. – S.Mandal Mar 28 '20 at 16:56