How exceptions work in Java
The Throwable hierarchy, what throw and catch compile to, the exception table and stack unwinding, the exact rules of finally, why constructing an exception is the costly part, OmitStackTraceInFastThrow, multi-catch and precise rethrow.
On this page
💥 What is an exception in Java?
An exception is an object that describes something going wrong, which the JVM carries up the call stack until some method says "I can handle this". When PaymentService.charge() executes throw new IllegalStateException("card expired"), normal execution stops at that line, and the JVM starts looking for a matching catch block, first in charge(), then in its caller, then in the caller's caller. If nobody catches it, the thread dies and prints a stack trace.
For this lesson, we'll focus on what actually happens between throw and catch: the Throwable family tree, the exception table that javac writes into every method, how the JVM unwinds the stack, the exact rules of finally, and why creating an exception costs more than throwing it. Interviewers love this topic because everyone has written try/catch, but very few candidates can say what the bytecode looks like or why a return inside finally is a bug.
Below the line (out of scope)
- Checked vs unchecked rules and the design debate: Checked vs unchecked exceptions
- Closing resources safely: try-with-resources
- Designing your own exception types: Custom exceptions and best practices
The 30-second answer
Everything you can throw extends Throwable, which splits into Error (JVM-level problems like OutOfMemoryError you shouldn't catch) and Exception (problems your code can handle); RuntimeException is the unchecked branch of Exception. throw compiles to the athrow instruction. Each method has an exception table of from, to, target, type rows; the JVM scans it for the throwing instruction, jumps to the first matching handler, and if none matches it pops the frame and repeats in the caller. A try costs nothing on the happy path. finally is copied by javac into every exit path, so it runs on return, on exception and on break, but not after System.exit() or a JVM crash. The expensive part is creating the exception, because the constructor calls fillInStackTrace() to walk the stack.
What you’ll learn
- Place any exception in the Throwable hierarchy and say whether it's checked:
Error,Exception,RuntimeException - Read a method's exception table in
javap -coutput and explain how the JVM unwinds the stack - Explain how
javaccopiesfinallyinto every exit path, why areturninfinallyswallows exceptions, and whenfinallydoesn't run - Explain why constructing an exception is expensive (
fillInStackTrace) and how to build a stackless one - Diagnose stack-trace-less NPEs caused by
-XX:+OmitStackTraceInFastThrow - Use multi-catch and precise rethrow correctly
Interview radar
Asked very often: What's the difference between Error, Exception and RuntimeException, and does finally always run?
Senior rounds probe: What does a try/catch compile to, why are exceptions slow, and what happens when finally contains a return?
Production angle: Logs full of java.lang.NullPointerException with no stack trace, caused by the JIT's OmitStackTraceInFastThrow optimisation.
01Picture it first: a fire alarm going up the floors of a building
Think of the call stack as a building where each floor is a method call. When something catches fire on the 5th floor (throw), that floor checks its own list of fire wardens (its exception table). If it has a warden trained for this kind of fire, the warden handles it and the floor keeps working. If not, everyone on the floor leaves (the frame is popped, finally blocks run on the way out) and the alarm moves down to the 4th floor. That repeats until a warden is found or the building (the thread) is empty.
Before we follow the alarm, let's get the family tree right. The diagram below shows the three branches of Throwable and which ones the compiler forces you to handle.
The Throwable hierarchy: Throwable splits into Error and Exception; Error holds unchecked JVM problems like OutOfMemoryError and StackOverflowError; Exception holds checked exceptions like IOException and SQLException, plus RuntimeException, the unchecked branch containing NullPointerException and IllegalArgumentException
| Branch | Meaning | Checked? | Should you catch it? |
|---|---|---|---|
Error |
The JVM or environment is broken: OutOfMemoryError, StackOverflowError, NoClassDefFoundError |
No | Almost never, maybe log at the very top |
Exception (not runtime) |
Recoverable conditions outside your control: IOException, SQLException, InterruptedException |
Yes | Yes, or declare it with throws |
RuntimeException |
Usually a bug or a broken precondition: NullPointerException, IllegalArgumentException, ClassCastException |
No | Fix the bug; catch only at boundaries |
Notice that "checked" is purely a compiler idea. The JVM treats every Throwable the same way, which is a fact we'll lean on in the next lesson.
02Under the hood: what javac writes for try, catch and finally
Try it yourself first
Before reading on, guess how many times the call to audit() appears in the bytecode of this method. One? Two? Three?
static int charge(int cents) {
try {
return pay(cents);
} catch (IllegalStateException e) {
return -1;
} finally {
audit();
}
}
Here's the real output of javap -c for that method, compiled with javac --release 21:
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Real world: the NullPointerException with no stack trace
- Advanced: why exceptions are slow, and how to make them cheap
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal
Further reading (9)
- JLS §11: Exceptions
- JLS §14.20: The try statement (try-finally, try-catch-finally)
- JVMS §2.10: Exceptions
- JVMS §6.5: athrow
- Throwable (Java SE 21 Javadoc)
- Oracle Java Tutorials: Exceptions
- Aleksey Shipilev: The Exceptional Performance of Lil' Exception
- JEP 358: Helpful NullPointerExceptions
- Baeldung: Exception Handling in Java