JVM如何知道程序抛出异常的行数?

4

我想知道JVM如何检测崩溃,具体地说,它如何知道在哪行代码崩溃。

以下是示例代码:

import java.util.ArrayList;

class Main {
  public static void main(String[] args) {


    ArrayList<String> crashMe = new ArrayList<String>(0);
    crashMe.get(1);
  }
}

以下是崩溃信息(通过repl.it使用OpenJDK 10.0.2):

    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)

到目前为止,所有预期的行为都是正常的。

但是JVM如何知道我在第8行崩溃了? 编译java代码时是否会忽略换行符等? jdk.internal包为什么要抛出异常,当它们除了JVM开发人员以外没有任何用处时?

提前感谢任何能够给我一些见解的人。


有点相关 - Sweeper
5
JVM知道在编译代码中抛出异常的指令的字节码索引(bci)。类文件具有LineNumberTable属性,将bci映射到源代码中实际的行号。这些信息是为了调试目的而故意保存在.class文件中。 - apangin
1个回答

6
但是JVM如何知道我在第8行崩溃了呢?
看一下java.lang.Throwable的构造函数:
public Throwable() {
    fillInStackTrace();
}

fillInStackTrace 方法使用 JVM 本身实现的本地代码来填充当前堆栈跟踪。 堆栈跟踪本身只是一个 StackTraceElement 数组,其中每个元素包含类、方法、文件名和代码路径中的行号,这些路径指引我们创建异常。 然后将堆栈跟踪存储在 Throwable 实例中,以便稍后打印。

顺便说一下,您可以创建一个 Throwable 并获取其堆栈跟踪,而无需实际抛出它。 因此,以下代码:

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()");
    }
}

将会输出:

Foo.main(Foo.java:4)
This is the end of main()

请注意,由于我们刚刚创建了一个异常而没有抛出它,因此会打印This is the end of main()。这就是从编译代码创建堆栈跟踪的原因。

在编译Java代码时,换行符等是否被忽略?

在编译时?是的。 在创建堆栈跟踪时?不是。字节码包含转换为该字节码的源代码指令的行号。

为什么jdk.internal包要抛出异常,对于除JVM开发人员以外的任何人都没有用呢?

首先,JVM开发人员也是人。他们应该和其他人一样拥有异常。

其次,您看到的异常似乎来自jdk.internal.util,但这只是因为ArrayList使用“内部”前提条件实用程序来检查边界。


很久以后评论:我的问题是关于内部异常的,我认为JVM开发人员应该有某种调试版本的JVM来进行调试,而不是将所有元数据打包到(在我的情况下)OpenJDK的发布版本中,这会减慢异常的创建速度(因为跟踪更长),使标准库变得更大,并且在加载时间和RAM使用方面略微影响。 - Icedude_907

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接