繁体   English   中英

为什么 Java 不告诉你哪个指针为空?

[英]Why doesn't Java tell you which pointer is null?

我一直想知道为什么 JVM 在抛出NullPointerException时不告诉您哪个指针(或更准确地说,哪个变量)为 null。

行号不够具体,因为有问题的行通常包含许多可能导致错误的变量。

是否有任何编译器或 JVM 标志可以使这些异常消息更有用?

这是因为取消引用总是在没有可用名称时发生。 该值被加载到操作数堆栈,然后传递给取消引用它的 JRE 操作码之一。 但是,操作数堆栈没有与空值关联的名称。 它所拥有的只是“空”。 使用一些聪明的运行时跟踪代码,可以派生出一个名称,但这会增加开销而价值有限。

因此,没有 JRE 选项可以打开空指针异常的额外信息。

在此示例中,引用存储在本地插槽 1 中,该插槽映射到本地变量名称。 但是取消引用发生在 invokevirtual 指令中,它只在堆栈上看到一个“空”值,然后抛出一个异常:

15 aload_1
16 invokevirtual #5 

同样有效的是数组加载后跟取消引用,但在这种情况下,没有名称映射到“空”值,只是另一个值的索引。

76 aload    5
78 iconst_0
79 aaload
80 invokevirtual #5

您也不能为每条指令静态分配名称 - 这个示例会产生大量字节码,但您可以看到取消引用指令将接收 objA 或 objB,并且您需要动态跟踪它以报告正确的指令,因为两个变量都流向相同的取消引用指令:

(myflag ? objA : objB).toString()

JIT 代码后,它只是本机指针数学,如果本机代码中的任何指针为 null,它就会抛出异常。 将该程序集反转回原始变量会对性能产生毁灭性的影响,并且考虑到 JIT 将生成的代码优化到不同的级别,通常甚至是不可能的。

从 Java 15 开始,它终于告诉你:看这个 JEP: https ://openjdk.java.net/jeps/358(它实际上是在 Java 14 中添加的,但默认情况下被禁用,现在在 Java 15 中默认启用) .

如果您将该行分成多行而不是在一行上进行多个方法调用,或者如果您在该行上设置一个断点并使用调试器逐步执行该行,您可以很容易地找出哪个引用为空。

如果

行号不够具体,因为有问题的行通常包含许多可能导致错误的变量。

那么我建议:

  1. 将该行分成多行并将可能的NullPointerException生成值分配给临时变量。
  2. 使用调试器并单步执行每个方法调用,直到找到导致问题的方法。

不幸的是,这正是 Java 的工作方式。

如果这是“您的”代码,则只需添加如下代码片段

if (foo == null) {
  throw new NullPointerException("foo == null");
}

在分配 foo 之后。 如果 foo 是参数,则立即在方法主体的开头检查,并抛出 IllegalArgumentException。

这应该可以帮助您澄清问题。

调试的时候可以在Eclipse中的空指针异常上加断点,得到异常的确切原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM