Reputation: 182
I am wondering about how the JVM detects crashes, specifically, how it knows which line of code it crashes on.
Here is a sample portion of code:
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
ArrayList<String> crashMe = new ArrayList<String>(0);
crashMe.get(1);
}
}
And this is the crash message (OpenJDK 10.0.2 via repl.it):
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 0
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.get(ArrayList.java:458)
at Main.main(Main.java:8)
All expected behavior so far.
But how does the JVM know that I am crashing on line 8? Are new lines and such ignored when compiling java code? Why does the jdk.internal package even bother throwing exceptions, when they are of no use to anyone but a JVM developer?
Thank you to anyone in advance who can offer me some insight.
Upvotes: 4
Views: 345
Reputation: 30285
But how does the JVM know that I am crashing on line 8?
Take a look at the constructor for java.lang.Throwable
:
public Throwable() {
fillInStackTrace();
}
That fillInStackTrace
method fills in the current stack trace using native code implemented in the JVM itself. The stack trace itself is simply an array of StackTraceElement
, each of which contains the class, method, filename, and line number in the code path that too us to the creation of the Exception. The stack trace is then stored in the Throwable
instance, and can later be printed.
By the way, you can create a Throwable
and get its stack trace without actually throwing it. So the following code:
public class Foo {
public static void main(String[] args) {
Throwable t = new Throwable();
for (StackTraceElement e : t.getStackTrace()) {
System.out.println(e);
}
System.out.println("This is the end of main()");
}
}
Will print:
Foo.main(Foo.java:4)
This is the end of main()
Note that This is the end of main()
is printed since we've just created an exception. We haven't thrown it. This is what enables the creation of the stack trace from compiled code.
Are new lines and such ignored when compiling java code?
When compiling? Yes. When creating a stack trace? No. The byte code contains the line number of the source code instruction that translated into that byte code.
Why does the jdk.internal package even bother throwing exceptions, when they are of no use to anyone but a JVM developer?
First of all, JVM developers are people too. They deserve to have exceptions just like everyone else.
Second, the exception you're seeing does seem to originate jdk.internal.util
, but it's just because the ArrayList
uses the "internal" preconditions utility to check for bounds.
Upvotes: 6