-
Notifications
You must be signed in to change notification settings - Fork 29
Throwing exceptions and errors in Java
In its simplest form, here's what happens when an error or exception is thrown: the class of error/exception, such as NullPointerException
(which is a class in java.lang
) is instantiated and initialized. It is then thrown, which is performed by the ATHROW
bytecode. As defined in the JVM specification, the ATHROW
bytecode checks to see whether there are any catch
processors for this exception in the present method or any of the predecessor frames on the JVM stack. If there is one, all intervening frames are removed from the stack and the logic of the catch
processing is executed. If none exists, then ATHROW
executes the default action (dump a stack trace) and exit the program.
The bytecode for a one-line program that throws an exception is:
0: new #7 // class java/lang/NullPointerException
3: dup
4: invokespecial #9 // Method java/lang/NullPointerException."<init>":()V
7: athrow
which simply instantiates the object, calls the constructor, and then executes ATHROW
.
Instantiating NullPointerException
requires going up all the superclasses, which starting from the bottom are:
java.lang.RuntimeException
-> java.lang.Exception
-> java.lang.Throwable
-> java.lang.Object
Of these, java.lang.RuntimeException
and java.lang.Exception
are primarily classes that call methods in java.lang.Throwable
. In fact java.lang.RuntimeException
is a class with no fields. It contains a variety of constructors, all of which call constructors in java.lang.Exception
. Exactly the same is true of java.lang.Exception
, which contains a simple cohort of constructors, all of which call metnods in java.lang.Throwable
, which is where all the action happens.
Throwable
is the superclass for both exceptions and errors. It consists of five constructors and 13 methods. Its primary responsibility, which is performed in its constructors, is to get and store the data for a stack trace.
It also handles the special case in which a Throwable
is being thrown by another Throwable
and handles how to reflect that in the stack trace.
That stack trace is an array of java.lang.StackTraceElement
entries. These entries are simply collections of fields:
String classLoaderName,
String moduleName,
String moduleVersion,
String declaringClass,
String methodName,
String fileName,
int lineNumber
One StackTraceElement
is generated for each frame in the JVM stack at the time the throw occurs.
Ultimately, instantiating a NullPointerException
results in an empty Throwable
that has been recast all the way down to the subclass. Then, NullPointerException
's constructor is called. This goes back up to Throwable
, which fills in the stack entries in the array of java.lang.StackTraceElement
s.
A pointer to that filled-in NullPointerException
is then placed on the stack and the ATHROW
bytecode is executed, as described above.