Feature request: NullPointer with several objects in same source code line - easily spot the object being null

Krystal Mo krystal.mo at oracle.com
Tue Mar 5 21:36:22 PST 2013


Hi Josef,

Generally it's hard to implement such feature in an efficient way. It 
may not be as simple as you might have expected.
Your best option is to make good use of 
java.util.Objects.requiresNotNull [1], which does throw an NPE and with 
an appropriate message that you can specify.

First of all, NPE is "null Pointer exception", not "null Object 
exception". Only Java references can be null; Java objects can't.

Second, what you're asking for is actually "let the JVM blame the 
variable or parameter causing the NullPointerException". Which isn't 
easy to do.
Not all references are stored in named variables; temporary ones don't, 
so there would be no "name" to map the NPE onto. e.g.

foo.bar.baz.someMethod(); // will throw NPE if bar is null
foo.bar().baz(); // will throw NPE if bar() returned null

Looking at the problem from the abstract conceptual JVM level, you can 
only dereference a Java reference on the operand stack (and thus throw 
NPE when the reference is null). Even if a reference came from a local 
variable (or method parameter), it's still not always statically-known 
which variable it came from. e.g.

public class Foo {
   public static int demo(boolean cond, String s1, String s2) {
     return (cond ? s1 : s2).length();
   }
}

Foo.demo() would be compiled into:

   public static int demo(boolean, java.lang.String, java.lang.String);
     flags: ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=3, args_size=3
          0: iload_0
          1: ifeq          8
          4: aload_1
          5: goto          9
          8: aload_2
          9: invokevirtual #2                  // Method 
java/lang/String.length:()I
         12: ireturn
       LineNumberTable:
         line 3: 0
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
                0      13     0  cond   Z
                0      13     1    s1   Ljava/lang/String;
                0      13     2    s2   Ljava/lang/String;
       StackMapTable: number_of_entries = 2
            frame_type = 8 /* same */
            frame_type = 64 /* same_locals_1_stack_item */
           stack = [ class java/lang/String ]

The only bytecode instruction that can throw a NPE is invokevirtual 
java.lang.String.length(), which pops a reference off the top of the 
operand stack, and dereference it to do the virtual method invocation. 
At that point, the reference on the top of the operand stack could 
either be from method parameter s1 or s2, depending on the value of 
cond. Since this can't be deduced statically, a JVM will have to some 
sort of flow tracking to know where a reference came from, which is hard 
and slow.

That said, it may be nice if the Throwable core library classes can 
expose more details of an exception, such as the bytecode index of the 
exception site. That way you'd at least get an idea which bytecode 
instruction went wrong, and use that as a starting point to manually 
analyze which reference was null.

- Kris

[1]: 
http://docs.oracle.com/javase/7/docs/api/java/util/Objects.html#requireNonNull(T, 
java.lang.String)

On 2013/2/21 10:07, Josef Lehner wrote:
> Dear Hotspot-Runtime team,
>
> every once in a while every Java programmer encounters a 
> NullPointerException and it is often hard to find out which object is 
> null, especially in production where debugging isn't possible.
>
> It would be fantastic if the jvm could blame the object causing the 
> NullPointerException.
>
> Thanks in advance.
>
> Best regards
>
> Josef
>
> String s1=null;
> String s2=null;
> s1.concat(s2.toString()); // Line 7
>
> Exception in thread "main" java.lang.NullPointerException <<< should 
> be something like >Exception in thread "main" 
> java.lang.NullPointerException: s2 is null<
>     at stuff.Example.main(Example.java:7)
>     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>     at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
>     at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>     at java.lang.reflect.Method.invoke(Method.java:487)
>     at 
> com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)



More information about the hotspot-runtime-dev mailing list