4

When will a StackOverError be thrown?

Or rather, when will it not be thrown?

For example, if we use the primitive operators +, +=, -, -=, == <, >, /, %, etc:

try {
     // operations +, +=, -, -=, == <, >, /, %, etc
} catch (java.lang.StackOverflowError e) {
     // will never occur?
}

Is there any guarantee that StackOverflowError will not be thrown?

Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • I am not a Java developer, but out of curiosity I need to ask: can you catch `StackOverflowError` in Java? (My reason for asking is that in .NET, as of version 2.0 `StackOverflowException` cannot be caught in a `try..catch` block.) – Fredrik Mörk Feb 13 '12 at 13:37
  • `@Pacerier`: I think I'd be more worried about exceptions *in general*, things like `OutOfMemoryError` for example. Worrying specifically about a `StackOverflowError` seems a bit...specific. – T.J. Crowder Feb 13 '12 at 13:40
  • @T.J.Crowder no I was equally worried about OOM as well, but that's a more "language-agnostic" question so I posted it at http://stackoverflow.com/q/9261705/632951. Thinking about it, an OOM seems to be very much like an SO, both are thrown when there's no memory left. One is thrown when there's no memory on the stack and the other is thrown when there's no memory *in general*. – Pacerier Feb 13 '12 at 14:23
  • @FredrikMörk Yes we can catch stackoverflows in Java, if you can't in .NET how do you recover when that occurs? – Pacerier Feb 13 '12 at 14:25
  • @Pacerier: Right. Note that the "no memory in general" usually doesn't include the stack situation, because in most environments the stack is pre-allocated (and so OOM can't be caused by running out of stack). Also, I suspect aioobe is right that a JVM implementation will tend to hold back a bit of resource to avoid running out of that resource when throwing a runtime exception. – T.J. Crowder Feb 13 '12 at 14:25
  • @T.J.Crowder I've demonstrated (in the update at http://stackoverflow.com/q/9261705/632951) that it is generally impossible for the JVM to reserve "infinite" memory. – Pacerier Feb 13 '12 at 14:45
  • @Pacerier: Of course it's impossible to reserve infinite memory. That's not the point. However, I didn't know JVMs may try to dynamically resize the stack, and so cause an OOM that way. – T.J. Crowder Feb 13 '12 at 14:48
  • @T.J.Crowder sorry I made a typo, I meant "it is generally impossible for the JVM to reserve *sufficient* memory". – Pacerier Feb 13 '12 at 14:52
  • @Pacerier: we don't ;) It's actually quite logical: in order to catch the exception and do something with it you would likely need to allocate some memory on the stack, but since the exception indicates that you are out of stack space you can't really do that. – Fredrik Mörk Feb 13 '12 at 16:42
  • @FredrikMörk Ok, so what's the solution? How do you log data to see the error at a later date? – Pacerier Feb 13 '12 at 17:04
  • @Pacerier: the solution is to write solid code where stack overflow errors don't occur. Be _very_ careful with exit criteria in recursive code, and _write tests_ to examine the exit criteria. To be honest, in nearly twenty years of programming stack overflow errors have never really been an issue. – Fredrik Mörk Feb 13 '12 at 18:06
  • @FredrikMörk But stack overflows will occur even without recursion. It's not uncommon for a thread pool to set the thread stack size to 32kb (to cater for more threads) or lower, it's hard to *guarantee* that stackoverflows will never occur http://stackoverflow.com/q/389219/632951 http://stackoverflow.com/q/1756285/632951 – Pacerier Feb 13 '12 at 18:19

5 Answers5

4

In theory, on some weird JVM + operator can be implemented using recursion internally by adding + 1 in a recursive loop. The other operators might also be implemented using internal method call.

I don't think it will ever be the case. In every typical JVM/architecture these operations are implemented using single operation/instruction. There are bytecode instructions for all these operators and I would expect they are translated 1:1 to assembly. But there are no guarantees in the JLS.

By the way the JavaDoc:

Thrown when a stack overflow occurs because an application recurses too deeply.

is not really correct. You can get StackOverflowError without any recursion - if you have very deep call tree and methods have very long argument list. However this is very hard to achieve in practical terms.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
4

The only reference to StackOverflowError in the Java Language Specification is the following:

15.12.4.5 Create Frame, Synchronize, Transfer Control

A method m in some class S has been identified as the one to be invoked.

Now a new activation frame is created, containing the target reference (if any) and the argument values (if any), as well as enough space for the local variables and stack for the method to be invoked and any other bookkeeping information that may be required by the implementation [...]. If there is not sufficient memory available to create such an activation frame, an StackOverflowError is thrown.

The JVM spec says the following:

StackOverflowError: The Java virtual machine implementation has run out of stack space for a thread, typically because the thread is doing an unbounded number of recursive invocations as a result of a fault in the executing program.

So, judging from the above statements...

I was wondering is it true that code which do not call any functions will never throw a java.lang.StackOverflowError?

...yes, that's true.

For example, if I use the operators +, +=, -, -=, ==, <, >, /, % etc on primitives (including long and double),

Right. None of those will ever (by themselves) invoke a method and should therefore not cause a StackOverflowError.

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • What happens if when throwing a SO, "there is not sufficient memory available to create such an activation frame" to create and throw *that* SO ? – Pacerier Feb 13 '12 at 13:32
  • Heh. If I were a JVM implementor, I would reserve that amount of memory for this purpose, i.e. throw the error whenever I reached a point where I ran the risk of not being able to throw the error anymore. – aioobe Feb 13 '12 at 13:54
  • True but if you reserved 100 units of memory to do that, and spent 1 unit on the first StackOverflow, what happens if in my SO catch block I do e.printStackTrace()? e.printStackTrace() requires memory too. Then you would spend another unit of memory to throw me another stack overflow (98 units left) and I catch that with a e.printStackTrace() so you throw me another StackOverflow (97 units left) and well that is caught and I wanted to e.printStackTrace() that as well.. If there's no way for you to "reclaim" the memory you have reserved you would run out of it anyway no matter.. – Pacerier Feb 13 '12 at 13:58
  • *what happens if in my SO catch block I do e.printStackTrace()?* -- Presumably a StackOverflowError would be thrown? No, you wouldn't have 98 units left, since A) objects are not allocated on the stack, they are allocated on the heap, B) No activation frame was created to enter the catch-block, so the stack is unchanged. – aioobe Feb 13 '12 at 14:38
  • At least with OpenJDK 7 (presumably Oracle Java 7 as well), @aioobe is correct in that the JVM will pre-allocate and pre-initialize enough memory for a single StackOverflowError that is reused wherever necessary (see [interpreterRuntime.cpp](http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/acb171a8d7d6/src/share/vm/interpreter/interpreterRuntime.cpp) `throw_StackOverflowError(JavaThread* thread)` as one supporting source example). – Go Dan Feb 13 '12 at 15:01
  • ".yes, that's true." Sadly, it is not. See my answer: http://stackoverflow.com/a/9277502/545127 – Raedwald Feb 25 '12 at 14:42
  • @aioobe, Btw, how does this answer match up with Raedwald's answer? – Pacerier Dec 28 '14 at 18:11
2

See the documentation:

Thrown when a stack overflow occurs because an application recurses too deeply.

So, when your application does not recurse at all (i.e. does not call methods), you won't get a StackOverflowError.

Of course, when we do not talk about primities only (where the basic operations are implemented directly using Java bytecode instructions), we easily run into StackOverflowErrors.

Sample:

int foo = 23;

foo = 23 + bar;

What if bar is an java.lang.Integer? Java will do automatic unboxing and that will lead to the following bytecode:

   0:   bipush  23
   2:   istore_1
   3:   bipush  23
   5:   getstatic   #3; //Field bar:Ljava/lang/Integer;
        v v v v v v v 
   8:   invokevirtual   #4; //Method java/lang/Integer.intValue:()I
        ^ ^ ^ ^ ^ ^ ^
   11:  iadd
   12:  istore_1
   13:  return

That is a (implicit) method invokation and could therefore cause a StackOverflow.

Johannes Weiss
  • 52,533
  • 16
  • 102
  • 136
  • Are you sure that calling i.e. "%" on doubles does not do an implicit method call. And that __could__ cause a SOE. Would be interesting to disassemble a piece of code and have a look. – mgaert Feb 13 '12 at 13:27
  • @JohannesWeiß, It's more of, is `drem` guaranteed **not** to cause the error? – Pacerier Dec 28 '14 at 18:11
2

For example, if I use the operators +, +=, -, -=, == <, >, /, % etc on primitives (including long and double)...Can we can guarantee there's no way a stackoverflow error would be thrown from that operation?

Yes, for the standard math and comparison operators. The key there was where you said "...on primitives..." Since Java doesn't allow operator overloading, and the JVM's implementation of these for primitives will not involve recursion, you can be sure. This would not necessarily be the case if you were talking about non-primitives, where certain of these operators might cause a call to non-JVM code (using + to append an object to a string, for instance, which would trigger the object's toString method).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • However if there is not sufficient memory to do the operations `+, +=, -, -=, == <, >, /, %`, wouldn't the JVM throw an SO? http://stackoverflow.com/a/9261320/632951 – Pacerier Feb 13 '12 at 13:35
  • @Pacerier: No. It might throw some *other* kind of exception, like an [`OutOfMemoryError`](http://docs.oracle.com/javase/7/docs/api/java/lang/OutOfMemoryError.html), but not an SO exception. – T.J. Crowder Feb 13 '12 at 13:38
  • but every `new` call is a method call which requires the stack which means every `new` call would throw a StackOverflow. So when the JVM wants to create an OOM (or another error) to throw to us, that `new` operation in turn throws a StackOverflow, then what happens? – Pacerier Feb 13 '12 at 13:43
  • 1
    What about `int a = 5 + foo` ? `foo` could be a `java.lang.Integer` that would unbox it using `java.lang.Integer.intValue()` which is a method invokation... – Johannes Weiss Feb 13 '12 at 13:47
  • @Pacerier: Okay, fair enough. If you're so close to out of stack that even just *one* additional stack frame will cause an SO exception, then any other kind of exception could potentially cause it as side effect. You're into seriously edge conditions here: First you have to posit a proximate cause (running out of memory, perhaps on a `String` `+` operation), then when trying to fail on that, it would have to run out of stack. And of course, what about the converse? An SO that causes an OOM? Do you **really** need to cater for these extreme situations? – T.J. Crowder Feb 13 '12 at 13:49
  • 1
    @JohannesWeiß: It's a good point, but again, pacerier said "primitives." An `Integer` is not a primitive, it's an object. As I pointed out, that proviso is key. – T.J. Crowder Feb 13 '12 at 13:50
  • yeah, exactly true (see my post), but it is really hard to identify! – Johannes Weiss Feb 13 '12 at 13:53
  • @T.J.Crowder Yes I was thinking of that as well.. I was thinking what happens when the JVM has no memory left to create an SO/OOM. Does it just hang? Or would it instead throw us a "null" and continue from that point? Are you aware if these things are specified? – Pacerier Feb 13 '12 at 14:16
  • Your answer states JVM's implementation of these for primitives will not cause SO error. Do you mind elaborating on that? How does it match up with Raedwald's answer? – Pacerier Dec 28 '14 at 18:10
0

is it true that code which do not call any functions will never throw a java.lang.StackOverflowError?

A StackOverflowError is-a VirtualMachineError. It transpires that there are no no-throw guarantees about VirtualMachineErrors. The Java Virtual Machine Specification says the following (emphasis added).

This specification cannot predict where internal errors or resource limitations may be encountered and does not mandate precisely when they can be reported. Thus, any of the VirtualMachineError subclasses defined below may be thrown at any time during the operation of the Java virtual machine: ... StackOverflowError

Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237