Errors and exceptions thrown by the Java Virtual Machine

Your code and the methods it calls may throw exceptions. But there are also various exceptions that may be thrown by the JVM itself rather than from within your code. Strictly speaking, some of these are Errors rather than Exceptions— in other words, it is not guaranteed that your code will actually be able to catch them. In many cases, they can in practice be caught if necessary.

Exception/ErrorTypical circumstances when thrownExample
NullPointerException Attempt to access a field or method on a null reference.
Object o = null;
o.toString();
			
Attempt to unbox a null Integer, Boolean etc
Boolean b = null;
if (b) {
  ...
}
Integer i = null;
if (i == 3) {
  ...
}
Attempt to synhcronize on a null reference
Object obj = null;
synchronized (obj) {
  ...
}
			
StackOverflowError

A chain of too many methods or constructors calling into one another: usually caused by a method or constructor that recursively calls itself without returning. This eventually causes the special area of memory known as the stack to run out.

Occasionally, a correctly functioning program that performs deep levels of recursion may still get this error, in which case it may be solved by increasing the thread's stack size. You can do this application-wide by setting the -Xss... JVM parameter, e.g. -Xss32m.

private void badMethod() {
    badMethod();
}
ArithmeticException Thrown by an arithmetic operation that is not permitted, generally an integer division by zero. (Note that floating point divisions by zero are allowed, since a float or double can represent infinity. If you are not careful, this can mean that bugs where you accidentally divide by zero are hard to track down...!)
int seconds = 0;
int rate = items / seconds;
OutOfMemoryError

Thrown when the JVM cannot allocate memory for a given object. In principle, this means that there is not a big enough area of heap space available, even after garbage collection and defragmentation (i.e. removing objects no longer rerferenced and earranging objects in memory to make a large enough space available).

It could also mean that a memory allocation request is simply invalid in principle (a request to create an array that is bigger than the JVM knows it can create). The getMessage() on the OutOfMemoryError itself will sometimes provide a more detailed reason such as Requested array size exceeds VM limit.

In practice, the situation can be complex in the case of a resource-intensive multithreaded application running on a modern server-class JVM. For example, it is possible to get an OutOfMemoryError when theoretically, space could have been made available, but the garbage collector is starved of CPU time to free up the memory.

Whether or not it is safe and possible to catch an OutOfMemoryError therefore depends very much on the situation: the failure to allocate memory for one specific object by one thread could actually be caused by a memory leak in an unrelated thread, or by the GC being starved of resources. Whether or not your application is left in a safe state when this occurs may be somewhat unpredictable. On the other hand, an OutOfMemoryError from a simple array allocation can generally be caught safely.

iint[] x = new int[1_000_000_000];
byte[] x = new byte[Integer.MAX_VALUE];
InterruptedException Usually, an InterruptedException is thrown when interrupt() is called on the thread in question, and it is currently in a blocking call such as Object.wait() or Thread.sleep(). Under extremely rare circumstances, the reason for the interruption could be from the OS itself. If you get an InterruptedException and you haven't specifically called interrupt() on the thread being interrupted, then bear in mind that other APIs may internally interrupt the threads they manage. If you are using the Executor framework, for example, note that calling cancel() on a scheduled task can optionally interrupt the task with a thread interrupt. See e.g. Future.cancel()
ExceptionInInitializerError Occurs if you have a static field or static block in your class which itself throws an exception, preventing your class from being initialised. In the example here, attempting to initialise the static field throws a PatternSyntaxException because the regular expression is malformed. This in turn means the class cannot be initialised, and so the first time it needs to be loaded, an ExceptionInInitializerError will be thrown.
lass MyClass {
  // Erroneous regex: will cause class initialisation to fail
  private static Pattern PATT = Pattern.compile("(");
}

new MyClass();
NoClassDefFoundError

In principle, this error is thrown if the JVM cannot find a class that it needs to load "in the normal course of execution", for example, because your code is attempting to create an object of the relevant class or is calling a static method on it, or refers to it in an instanceof etc. This is different to a ClassNotFoundException, which occurs when you deliberately try to load a class by calling Class.forName(...).

In practice, the JVM may also throw a NoClassDefFoundError to indicate that the class was previously found successfully, but that an error occurred when attempting to initialise it the first time. On that initial load, an ExceptionInInitializerError would have been thrown. But from then on, the JVM will not attempt to re-initialise it, and will instead throw a NoClassDefFoundError with the message Could not initialize class.

class MyClass {
  // Erroneous regex: will cause class initialisation to fail
  private static Pattern PATT = Pattern.compile("(");
}

// Class loaded but not initialised
try {
  new MyClass();
} catch (Throwable t) {}

// Throws a NoClassDefFoundError
new MyClass();
IllegalMonitorStateException Caused when you attempt to call wait() on an object but you have not synchronized on that object. A call to wait() should be in a block of code that synchronizes on the object in question. (The synchronization lock will be temporarily released during the actual wait then re-acquired by the time the wait() call returns.)
obj.wait();
AbstractMethodError This error means that your code has attempted to call an abstract method, in other words a method of an abstract class or interface that the subclass should define but does not. If you get an AbstractMethodError, the likely reason is that some of your code was compiled against one version of a class or jar and then running against a different version: at compile time, the method definition was present, but at runtime, it is absent (or its signature no longer matches). Subtly, this could include the case where conflicting definitions of a class are present in different jars pathed into your classpaths, with one version being picked up at compile time and the other and runtime. This type of error could occur, for example, if you have multiple versions of third party libraries pathed in.

If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.