Ruby Exception Handling

Ruby Exception Handling: SystemStackError

Today our journey through the Ruby Exception Handling series continues with one of the last remaining Ruby errors left to discuss: SystemStackError. This little baby pops up when code gets a little recursion-happy and ends up causing a stack overflow.

In this article we’ll examine the SystemStackError in more detail including where it sits within the Ruby Exception class hierarchy and how it might be raised in day-to-day coding. Let’s get crackin’!

The Technical Rundown

  • All Ruby exceptions are descendants of the Exception class, or a subclass therein.
  • SystemStackError is the direct descendant of the Exception class.

When Should You Use It?

Understanding what might cause a SystemStackError is just a matter of understanding what stack overflow means in the realm of development. Thus, we’d be well served to spend a few minutes refreshing ourselves of what stack overflow is and how it typically occurs. Most applications are allocated a specific range of memory addresses during execution. These addresses are pointers to bytes of data (memory) that the application is allowed to use. By extension, this memory assignment creates what is known as an address space, which is a specific quantity and range of memory addresses that can be safely used by the application in order to store and manipulate memory.

Since available memory is finite (for now, anyway) the address space assigned to an application is restricted within certain bounds. The actual size of this address space depends on many factors, but the result is that an application is allocated — and can, therefore, only use — a certain amount of memory before it runs out. In many cases the built-in garbage collector process within the programming language or framework will frequently gather up previously-used-but-no-longer-required address spaces and empty their contents (taking out the garbage, so to speak), which frees up more memory space to be used by future processing.

However, in some cases, the application’s code may attempt to perform tasks that require additional memory beyond what was allocated by the address space assigned to the process in the first place. 9 times out of 10, when this happens the application generates a stack overflow error which, in the case of Ruby, means raising a SystemStackError.

Now that we’re refreshed on stack overflows we can dive into a bit of simple code and see how they typically occur in Ruby. Probably the most common anti-pattern that often leads to stack overflows is the use of recursion. Recursion is the practice of calling a method within the code of said method. This practice is typically used when there exists an end goal (something the recursive method is aiming to eventually accomplish), combined with the ability to reduce the workload (and code overhead) of accomplishing that end goal.

For our examples here we have a MathClass that includes a couple simple methods called recursion and double:

The problem with recursion is if you’re not careful your code may have formed an infinite loop, which is what we have in the example method above. There’s no checks or balances to ensure that the recursive loop breaks at some reasonable point. Therefore, the Ruby engine has no choice but to eventually halt execution itself by raising a SystemStackError, as we see in the output:

Our other example method is dubbed the doubler (sorry for the terrible pun):

Here we have another recursive method call in which doubler invokes itself, but we’ve also added some extra functionality to experiment with trying to run out of memory via means other than stack overflow. In this case we’re just doubling the value class attribute every iteration and then storing each entry in the data attribute array. Since our value attribute will be ever-increasing in size that’s one angle of attack to try to see if Ruby runs out of memory. The other technique is to maintain an ever-growing array in the form of the data attribute.

As it turns out, Ruby doesn’t much care about our pitiful attempts to use up all the memory via (relatively small) numeric calculations and array storages, and thus execution of the double method results in the exact same number of recursive iterations as the recursion method before Ruby gives up and raises a SystemStackError:

To get the most out of your own applications and to fully manage any and all Ruby Exceptions, check out the Airbrake Ruby exception handling tool, offering real-time alerts and instantaneous insight into what went wrong with your Ruby code, including integrated support for a variety of popular Ruby gems and frameworks.