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

时间:2009-07-22 21:05:40

标签: java debugging nullpointerexception

我一直想知道为什么当抛出NullPointerException时,JVM没有告诉你哪个指针(或更确切地说,哪个变量)为空。

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

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

7 个答案:

答案 0 :(得分:49)

这是因为当没有可用的名称时,始终会发生取消引用。该值将加载到操作数堆栈上,然后传递给解除引用的JRE操作码之一。但是,操作数堆栈没有与空值关联的名称。它只是'null'。使用一些聪明的运行时跟踪代码,可以派生一个名称,但这会增加有限值的开销。

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

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

15 aload_1
16 invokevirtual #5 

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

76 aload    5
78 iconst_0
79 aaload
80 invokevirtual #5

你不能静态地为每条指令分配名称 - 这个例子产生了很多字节码,但你可以看到解除引用指令将接收到objA或objB,你需要动态地跟踪它以报告右边一个,因为两个变量都流向相同的解引用指令:

(myflag ? objA : objB).toString()

答案 1 :(得分:12)

一旦你JIT代码,它只是本机指针数学,如果本机代码中的任何指针为null,它会抛出异常。将组件反转回原始变量会产生破坏性的性能影响,并且考虑到JIT将生成的代码优化到不同的级别,通常甚至都不可能。

答案 2 :(得分:1)

自Java 15起,它终于可以告诉您!请参见此JEP:https://openjdk.java.net/jeps/358(实际上是在Java 14中添加的,但默认情况下已禁用,现在在Java 15中默认情况下已启用)。

答案 3 :(得分:0)

如果将行分成多行而不是在一行上进行多次方法调用,或者如果在该行上设置断点并使用调试器逐步执行该行,则可以非常容易地确定哪个引用为null。

答案 4 :(得分:0)

如果

  

行号不够具体   因为违规行可以经常   包含许多可能的变量   导致错误。

然后我建议:

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

答案 5 :(得分:0)

遗憾的是,这正是Java的工作方式。

如果这是“你的”代码,那么只需添加像

这样的代码段
if (foo == null) {
  throw new NullPointerException("foo == null");
}
分配foo后立即

。如果foo是一个参数,那么立即在方法体的开头检查,并改为抛出IllegalArgumentException。

这可以帮助你澄清问题。

答案 6 :(得分:0)

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