Teóricamente, cualquier tipo de desreferenciación de puntero nulo en Java desencadena un NullPointerException
, que puede manejarse dentro de Java como cualquier otra excepción. Esto no significa que sea bueno : en la práctica, es bastante difícil recuperarse de tal excepción, excepto eliminando la parte defectuosa (es decir, dejar que el hilo de llamada muera o volver a un retén). toda la cláusula). Seguir un puntero nulo es similar a anular un búfer: sigue siendo un error; La diferencia entre Java y los lenguajes no protegidos (como C) es que Java es más gracioso sobre las consecuencias de ese error: en particular, la excepción propagará la pila de llamadas, liberando monitores (las palabras clave synchronized
) y ejecutando las cláusulas finally
, y solo se verá afectado el subproceso defectuoso.
En la práctica, sin embargo, pueden surgir algunos problemas. Cómo la JVM atrapa las referencias erróneas de punteros nulos depende de la implementación de JVM; pero lo que suelen hacer las implementaciones es lo siguiente: simplemente siguen el puntero. Si el puntero es null
, esto conduce a un acceso a la memoria en la primera página de la memoria, lo cual está prohibido por el sistema operativo. El sistema operativo detecta la aparición (a través de la MMU) e informa a la aplicación de una manera relativamente brutal (en sistemas similares a Unix, esta es una señal SIGSEGV
). La JVM recibe la información y la transforma en un NullPointerException
.
La conversión de señal a excepción es algo complejo porque la JVM solo recibe la dirección del código de operación ofensiva, que se encuentra en algún lugar en el intérprete de bytecode o en el código binario producido por JIT. La JVM debe ubicar el método y la pila de hilos. Esto tiene algunas buenas interacciones con cómo el sistema operativo pone las cosas en la memoria en primer lugar. Tal código es bastante estable en la actualidad, en plataformas convencionales. Sin embargo, en una combinación menos común (el JDK de Sun portado a FreeBSD), a veces tuve desreferencias de puntero nulo para las cuales el JVM no pudo localizar la pila de llamantes (porque el código de JVM estaba sintonizado para Linux, no para FreeBSD, y aparentemente estaba haciendo una suposición sobre dónde debería estar la pila, suposición que no siempre se cumplió en FreeBSD). La consecuencia fue la finalización inmediata de JVM, de la manera más ingrata.
Otro posible error es que la desviación del puntero nulo se atrapa porque el acceso se encuentra en algún lugar de una página "prohibida"; A saber, la primera página del espacio de direcciones. Sin embargo, al acceder a un campo de instancia, la JVM generalmente accede directamente al elemento de datos correcto: si la referencia es, a nivel de código nativo, un puntero para direccionar x , y el campo está en desplazamiento n en la estructura del objeto, entonces el código leerá los bytes en la dirección x + n (esto realmente depende de cómo JVM representa los objetos internamente, pero una estructura tipo C es común). Si la clase tiene muchos campos, n puede ser más grande que el tamaño de una página, en cuyo caso el acceso de lectura no caerá en la primera página, sino en la segunda. Lo que sucede en esa situación depende del sistema operativo.
La situación del "campo en alto desplazamiento" no se presenta con las matrices, ya que los accesos a la matriz se controlan por longitud y la "longitud" es generalmente un tipo de campo que está cerca del encabezado del objeto. Además, hay un límite en el número de campos en una clase de Java (hay un límite absoluto a 65535 en el formato de archivo de clase, en realidad menor que eso porque cada campo debe tener un nombre y un tipo, que también están en la clase formato de archivo y llegará a otros límites antes de eso). Por lo tanto, el problema no surgirá en los sistemas "comunes", por ejemplo, Un Linux ejecutando Sun / Oracle VM. Esto es principalmente algo que los implementadores de máquinas virtuales deben tener en cuenta.
Por lo tanto, mi consejo es considerar NullPointerException
como un error grave, como un desbordamiento de búfer (que es IndexOutOfBoundsException
en Java). La seguridad de la máquina virtual le ayudará a depurar su código (los seguimientos de pila son realmente útiles) y, si no realizó la depuración suficiente antes de la implementación, es probable que guarde su aspecto. Pero un error es un error.