Java Errors
›Short versionOpen if you only have a minute.
- Throwable is the root of every throw. Error and Exception are its two children, and they describe different kinds of trouble.
- An Error means the runtime is in trouble: memory, the call stack, or a class that cannot be linked. Application code rarely has a safe way to continue after one.
- An Exception means an operation failed in a way a caller might handle: a missing file, a rejected query, a broken argument.
- Error is unchecked. You do not declare it, and that is not an invitation to catch it in ordinary methods.
- OutOfMemoryError and StackOverflowError are symptoms. The useful work is to change the allocation or the recursion that produced them.
When a Java program fails, the object that leaves the method is always a . The first split under that class is the one this page is about. An and an travel by the same throw and catch machinery, and they ask for different decisions.
Two branches, two responsibilities
Error is for the environment the program runs in. The heap cannot satisfy an allocation. A thread's stack is full. A class that compiled against another class cannot find it at runtime, which surfaces as NoClassDefFoundError. A static initializer threw, and the runtime wrapped that in ExceptionInInitializerError. These are reports that the process, or the linkage it depends on, is no longer sound.
Exception is for the work the program was doing. Opening a file, parsing a number, withdrawing from an account, calling a method with a null it promised not to receive. Someone in the call chain may retry, substitute a default, tell the user, or record the failure and stop that request. The later pages in this section are about that branch: checked and unchecked exceptions, catch clauses, propagation, and cleanup.
The compiler treats Error as unchecked. A method that might run out of memory does not say throws OutOfMemoryError, and callers are not forced to catch it. The language is refusing to pretend that every method has a plan for a broken runtime. The same silence applies to RuntimeException, for a different reason: those are usually bugs in the calling code, and a throws clause on every method that touches an object would be noise. Both are unchecked. Only one of them is an Exception.
A stack that never stops growing
A is the easier of the two common errors to see in a small program. Each call pushes a frame. This method pushes one and then calls itself, so the frames never come off.
public class Endless {
static void again() {
again();
}
public static void main(String[] args) {
again();
}
}The thread dies with StackOverflowError. The trace shows again repeated until the runtime stops printing frames. A real recursion needs a case that returns without calling itself, and an argument that moves toward that case. When the depth depends on input you do not control, an explicit stack on the heap, or a loop, keeps the thread's call stack a fixed size.
Memory the process cannot get
An means some allocation did not complete. This loop asks for another million ints on every pass and keeps every array reachable, so the garbage collector has nothing to reclaim. Eventually the heap allocation fails.
import java.util.ArrayList;
import java.util.List;
public class HeapEater {
public static void main(String[] args) {
List<int[]> held = new ArrayList<>();
while (true) {
held.add(new int[1_000_000]);
}
}
}The message often names the space that filled, such as the Java heap or metaspace. That name tells you where to look. A heap error sends you to what the program retains: unbounded caches, large lists built from a stream that could have been processed one record at a time, objects kept alive by a static collection. A metaspace error sends you to class loading. unable to create native thread sends you to thread count and the operating system's limit, which is a different shortage wearing the same class name.
What you leave uncaught
A catch clause whose type is Throwable or Error accepts this whole branch. The compiler allows it. The method then owns failures it cannot repair: linkage errors, assertion failures (AssertionError extends Error), and memory errors, mixed in with the exceptions the method actually knows how to handle.
try {
handleRequest(request);
} catch (Throwable t) {
log(t);
// The request is finished either way.
// Continuing as if the JVM were healthy hides the failure.
}Log at that boundary if you need the trace, then let the thread or the process end. The work of an OutOfMemoryError is a heap dump or an allocation profile, and a change to what is retained. The work of a StackOverflowError is the recursion. Neither problem is solved by a handler that prints the name and moves on to the next item in the batch.
A method allocates a large array and never declares throws OutOfMemoryError. The compiler accepts it. Does that mean the caller is supposed to catch the error?
No. OutOfMemoryError is an Error, so it is unchecked. The missing throws clause means the language will not force every caller to acknowledge a failure of the runtime. The caller's ordinary obligation is still to exceptions: the conditions the method can name and that some layer can answer. An allocation failure belongs to capacity and object lifetime, not to a catch block beside the business rules.