What is at the top of the exception hierarchy?
At the very top of the hierarchy of all objects that can be thrown and caught is the class java.lang.Throwable.
Junior Level
Root of the hierarchy - java.lang.Throwable
At the very top of the hierarchy of all objects that can be thrown and caught is the class java.lang.Throwable.
Object
└── Throwable
├── Error (fatal JVM errors)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── NoClassDefFoundError
│
└── Exception (application errors)
├── RuntimeException (unchecked)
│ ├── NullPointerException
│ └── IllegalArgumentException
│
└── Checked Exceptions
├── IOException
└── SQLException
Two main branches
Error - fatal JVM problems:
OutOfMemoryError- ran out of memoryStackOverflowError- infinite recursion- Don’t catch them - system is in an unstable state
Exception - application errors:
- Can and should be handled
- Divided into checked and unchecked
Error - problems that the application cannot fix (memory, stack). Exception - problems that it can. The separation in the hierarchy allows catching Exception without catching Error.
Example
// Throwable - parent of all exceptions
try {
// some code
} catch (Throwable t) {
// Catches EVERYTHING, including Error (usually you don't do this)
System.err.println("Caught: " + t.getMessage());
}
Middle Level
Why does Throwable implement Serializable?
Serialization - converting an object to bytes for network transmission or storage. This is needed for RMI (remote method invocation), JMX, caching. An exception must be serializable to “travel” between JVMs.
This is critical for distributed systems (RMI, JMX). When an exception occurs on the server, the JVM serializes it along with the stack trace and sends it to the client.
Key subclasses of Error
OutOfMemoryError- different subtypes:Java heap space- heap is fullMetaspace- no memory for class metadataUnable to create new native thread- OS denied threads
-
ThreadDeath- thrown onThread.stop()(deprecated). This is anErrorso that a normalcatch (Exception e)doesn’t swallow it. NoClassDefFoundError- class was present at compile time but missing at runtime. Environment integrity is broken.
The problem of propagation through ExecutorService
When an exception occurs inside a pool thread, it is caught by ExecutorService and stored inside the Future:
Future<?> future = executor.submit(() -> {
throw new RuntimeException("Task failed!");
});
// Exception is "hidden" inside Future
try {
future.get(); // ExecutionException is thrown here
} catch (ExecutionException e) {
System.err.println("Cause: " + e.getCause()); // RuntimeException
}
// ExecutorService catches all exceptions from tasks and stores them inside Future.
// When future.get() is called, the exception is wrapped in ExecutionException.
// To get to the cause: e.getCause().
fillInStackTrace()
fillInStackTrace() - a native method (written in C/C++, not Java). It walks the call stack at the OS level, creating a snapshot of all frames. This is expensive: requires transition between Java and native code + walking the entire stack.
When getStackTrace() is called, the native structure is converted to StackTraceElement[] array - this is a lazy optimization, but filling the native structure is still expensive.
Senior Level
Under the Hood: hidden backtrace field
Inside Throwable there is a field private Object backtrace, filled by the native method fillInStackTrace(). It holds a reference to the native call stack in JVM memory.
Serialization Pitfall: during serialization, the native backtrace is lost, and the JVM converts it to an array of Java objects. This increases packet size and CPU load.
Disabling stack trace for performance
public class FastException extends RuntimeException {
public FastException(String message) {
// enableSuppression = true, writableStackTrace = false
super(message, null, true, false);
}
}
Speeds up creation by tens of times - native stack walk is not executed.
Immutable Exceptions
In high-load systems, static instances are created:
public static final MyException INVALID_DATA =
new MyException("Invalid data", null, true, false);
Does not create new objects, but logs will have identical stack traces.
Suppressed Exceptions
Introduced in Java 7 for try-with-resources. If an exception occurs in try, and a second one - when closing the resource in finally, the first becomes the main one, the second is added as suppressed:
try {
resource.doWork(); // IOException #1
} finally {
resource.close(); // IOException #2
}
// IOException #1 - main, #2 - suppressed
Diagnostics
- Never do
catch (Throwable t)in business logic - will catchOutOfMemoryError - Root Cause Analysis - use
ExceptionUtils.getRootCause()to find the root cause - Distributed systems - at the service boundary, extract the message and error code, pass only DTO
Interview Cheat Sheet
Must know:
- Root of hierarchy -
java.lang.Throwable(notException) - Two branches:
Error(fatal JVM problems) andException(application errors) - Error -
OutOfMemoryError,StackOverflowError,NoClassDefFoundError- don’t catch - Exception is divided into
RuntimeException(unchecked) and checked exceptions fillInStackTrace()- native method, walks call stack, expensive operation- Suppressed exceptions - Java 7 mechanism for try-with-resources (saves second exception)
- Disabling stack trace (
writableStackTrace = false) speeds up creation by tens of times - During serialization, native
backtraceis lost - converted to Java objects
Frequent follow-up questions:
- Why does Throwable implement Serializable? - For RMI, JMX, caching - exceptions “travel” between JVMs
- What happens if you catch OutOfMemoryError? - JVM may be in unstable state, next operation will fail
- What are suppressed exceptions? - When try and close() both threw two exceptions, the second is added to the suppressed list of the first
- Why not create immutable exceptions for everything? - Identical stack trace makes debugging harder
Red flags (NOT to say):
- “Root of hierarchy is Exception” - no, Throwable
- “I catch Throwable in business logic for reliability” - it will catch OutOfMemoryError
- “fillInStackTrace() is a normal Java method” - no, it’s native, walks stack at OS level
Related topics:
- [[What is Throwable]]
- [[What is the difference between Error and Exception]]
- [[What are suppressed exceptions]]
- [[What is a stack trace]]
- [[What is try-with-resources]]