Theoretically, any kind of null-pointer dereferencing in Java triggers a NullPointerException
, which can be handled within Java just as any other exception. This does not mean that this is good: in practice, it is quite hard to recover from such an exception except by removing the faulty part (i.e. letting the calling thread die, or getting back to a catch-all clause). Following a null pointer is akin to overrunning a buffer: it is still a bug; the difference here between Java and non-protected languages (such as C) is that Java is more graceful about the consequences of that bug: in particular, the exception will propagate back the call stack, releasing monitors (the synchronized
keywords) and executing the finally
clauses, and only the faulty thread will be affected.
In practice, however, a few issues may arise. How the JVM traps null pointer dereferences is up to the JVM implementation; but what implementations usually do is the following: they just follow the pointer. If the pointer is null
, then this leads to a memory access in the first page of memory, which is access-forbidden by the OS. The OS catches the occurrence (through the MMU) and informs the application in a relatively brutal way (on Unix-like systems, this is a SIGSEGV
signal). The JVM receives the information, and transforms it into a NullPointerException
.
The conversion from signal to exception is a complex thing because the JVM only receives the offending opcode address, which is somewhere either in the bytecode interpreter, or in the JIT-produced binary code. The JVM must locate the method and the thread stack. This has some fine interactions with how the OS put things in memory in the first place. Such code is quite stable nowadays, on mainstream platforms. However, on a less common combination (Sun's JDK ported to FreeBSD), I sometimes had null pointer dereferences for which the JVM was unable to locate the caller stack (because the JVM code was tuned for Linux, not for FreeBSD, and was apparently making an assumption on where the stack should be, assumption which was not always fulfilled on FreeBSD). Consequence was immediate JVM termination, in a most ungraceful way.
Another possible gotcha is that the null pointer dereference is caught because the access falls somewhere in a "forbidden" page; namely, the first page of the address space. However, when accessing an instance field, the JVM usually accesses directly the right data element: if the reference is, at native code level, a pointer to address x, and the field is at offset n in the object structure, then the code will read bytes at address x+n (this really depends on how the JVM represents objects internally, but a C-like structure is common). If the class has many fields, then n may be greater than the size of a page, in which case the read access will not fall on the first page, but the second. What happens in that situation depends on the operating system.
The "field at high offset" situation does not arise with arrays, because array accesses are length-checked and the "length" is usually a kind of field which is close to the object header. Also, there is a limit on the number of fields in a Java class (there is an absolute limit to 65535 in the class file format, actually lower than that because each field must have a name and a type, which are also in the class file format and will hit other limits sooner than that). So the problem will not arise on "common" systems, e.g. a Linux running Sun/Oracle VM. This is mostly something that VM implementers must be aware of.
So my advice is to consider NullPointerException
as a serious bug, just like a buffer overflow (which is IndexOutOfBoundsException
in Java). The VM safety will help you debug your code (stack traces are really handy) and, if you did not debug enough before deploying, it will probably save your skin. But a bug is a bug.