Java Exception Handling

Java Exception Handling – StackOverflowError

Making our way through our in-depth Java Exception Handling series, today we’ll dig into the Java StackOverflowError. As with most programming languages, the StackOverflowError in Java occurs when the application performs excessively deep recursion. However, what exactly qualifies as “excessively deep” depends on many factors.

In this article we’ll explore the StackOverflowError a bit more by first looking where it resides in the overall Java Exception Hierarchy. We’ll also look at a simple, functional code sample that will illustrate how deep recursion can be created, and what might cause a StackOverflowError in your own code. Let’s get going!

The Technical Rundown

All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

When Should You Use It?

Before we look at what might cause a StackOverflowError in Java code, let’s first take a moment to review what a stack overflow actually is. Most applications are allocated a range of memory addresses that the application can use during execution. These addresses are stored and used as simple pointers to bytes of data (i.e. memory). This collection of addresses is known as the address space assigned to the application, and it contains a specific range of memory addresses that can be safely used by the application.

Unfortunately, at least for the foreseeable future, available memory is a finite resource. A Java application is limited to the bounds of its assigned address space. Processes like garbage collection will constantly free up memory that is no longer in use, but, by and large, there is a limited quantity of memory addresses available to any given application.

When an application attempts to use memory outside of its assigned address space a stack overflow error typically occurs. The runtime that is handling the application cannot safely allow said application to use memory that hasn’t been assigned to it, so the only logical course of action is to throw an error of some sort. In the case of Java, this is where the StackOverflowError comes in.

There are many different ways a stack overflow can occur within any given application, but one of the most common (and easily understood) is infinite recursion. This essentially means that a function or method is calling itself, over and over, ad nauseam. Different languages handle infinite recursion differently, but the Java Virtual Machine (JVM) handles infinite recursion by eventually throwing a StackOverflowError. To illustrate this behavior our example code is quite simple, primarily performed in the Iterator class:

As you can see, we have a few private members, along with the increment() method, which attempts a simple task: Iterate the count field, then call itself again. We also catch the potential errors (or Throwables) that might come up from this process.

Let’s create a new instance of Iterator and start the recursive process by calling the increment() method:

Executing the few lines of code above produces the following output:

We can see that a StackOverflowError was thrown, as expected, and it took about 7,200 iterations before the error occurred, with a total processing time of about 5.2 milliseconds. This is just one test, so let’s run it a few more times and record the results:

What’s immediately interesting is that, while a StackOverflowError is thrown every time, the number of recursive iterations necessary to cause the error changes every time, but within a reasonably similar range. The reason for this difference is due to the vast quantity of different factors within the system when execution occurs. For example, the JVM I’m testing this on is Windows 10 64-bit with 16GB of memory, but if we run this application on other machines (with different JVM configurations), we might see completely different iteration counts and/or elapsed times.

The Airbrake-Java library provides real-time error monitoring and automatic exception reporting for all your Java-based projects. Tight integration with Airbrake’s state of the art web dashboard ensures that Airbrake-Java gives you round-the-clock status updates on your application’s health and error rates. Airbrake-Java easily integrates with all the latest Java frameworks and platforms like Spring, Maven, log4j, Struts, Kotlin, Grails, Groovy, and many more. Plus, Airbrake-Java allows you to easily customize exception parameters and gives you full, configurable filter capabilities so you only gather the errors that matter most.

Check out all the amazing features Airbrake-Java has to offer and see for yourself why so many of the world’s best engineering teams are using Airbrake to revolutionize their exception handling practices!