NullPointerException en Java sin StackTrace

333

He tenido instancias de nuestro código Java atrapar a NullPointerException, pero cuando trato de registrar el StackTrace (que básicamente termina llamando Throwable.printStackTrace()), todo lo que obtengo es:

java.lang.NullPointerException

¿Alguien más se ha encontrado con esto? Traté de buscar en Google "java null pointer empty stack trace" pero no encontré nada como esto.

Edward Shtern
fuente
¿Cuál es el contexto? ¿Hay múltiples hilos involucrados? He tenido problemas al intentar obtener el seguimiento de la pila de una excepción en un SwingWorker.
Michael Myers
No hay hilos involucrados aquí, simplemente el viejo Java.
Edward Shtern
1
@Bozho - no - todavía no estoy seguro de cómo reproducir el NullPointer.
Edward Shtern
1
relacionado: stackoverflow.com/questions/1076191/…
Joshua Goldberg
Más información sobre -XX:-OmitStackTraceInFastThrowen el dup: stackoverflow.com/questions/4659151/…
Vadzim

Respuestas:

407

Probablemente esté utilizando el HotSpot JVM (originalmente de Sun Microsystems, luego comprado por Oracle, parte de OpenJDK), que realiza mucha optimización. Para recuperar los rastros de la pila, debe pasar la opción -XX:-OmitStackTraceInFastThrowa la JVM.

La optimización es que cuando se produce una excepción (generalmente una NullPointerException) por primera vez, se imprime el seguimiento completo de la pila y la JVM recuerda el seguimiento de la pila (o tal vez solo la ubicación del código). Cuando esa excepción ocurre con suficiente frecuencia, el seguimiento de la pila ya no se imprime, tanto para lograr un mejor rendimiento como para no inundar el registro con trazas de pila idénticas.

Para ver cómo se implementa esto en HotSpot JVM, tome una copia y busque la variable global OmitStackTraceInFastThrow. La última vez que miré el código (en 2019), estaba en el archivo graphKit.cpp .

Roland Illig
fuente
1
Gracias por el consejo. ¿Alguna idea de si hay problemas ocultos para pasar esta opción (parece bastante inocuo siempre que mi aplicación no arroje un montón de excepciones)?
Edward Shtern
No hay trucos ocultos que yo sepa. Cuando observa el código fuente del Hotspot, puede ver que esta opción solo se usa en un lugar (graphKit.cpp). Y eso me parece bien.
Roland Illig
34
Pensé que agregaría la información adicional que cuando el seguimiento de la pila se optimiza, es porque se ha manejado completamente al menos una vez: jawspeak.com/2010/05/26/…
sharakan
1
Estoy ejecutando un OpenJDK JVM, versión 1.8.0u171 (Debian 9), y parece aceptar la -XX:-OmitStackTraceInFastThrowbandera también. Todavía tengo que confirmar si esa fue la razón por la que tampoco estaba imprimiendo rastros de la pila (por ejemplo, usando e.printStackTrace), pero parece muy probable. He ampliado la respuesta para reflejar este descubrimiento.
Chris W.
En nuestro caso, las primeras 125 excepciones tenían un seguimiento de la pila, y luego el resto en 3 rotaciones de archivos de registro no tenía ninguno. Esta respuesta fue muy útil para encontrar al culpable.
sukhmel
61

Como mencionaste en un comentario, estás usando log4j. Descubrí (sin darse cuenta) un lugar donde había escrito

LOG.error(exc);

en lugar del típico

LOG.error("Some informative message", e);

por pereza o tal vez simplemente no pensar en ello La parte desafortunada de esto es que no se comporta como esperabas. La API del registrador realmente toma Object como primer argumento, no una cadena, y luego llama a toString () en el argumento. Entonces, en lugar de obtener el bonito y bonito seguimiento de la pila, simplemente imprime toString, que en el caso de NPE es bastante inútil.

¿Quizás esto es lo que estás experimentando?

Steven Schlansker
fuente
+1: Esto explicaría el comportamiento descrito, y usted no es el único que descubrió esto :)
Peter Lang
44
En realidad, tenemos una política estándar de nunca usar el primer formulario anterior (LOG.error (exc);): siempre usamos la firma de 2 parámetros para agregar alguna declaración descriptiva a los registros en lugar de solo un seguimiento de pila sin formato.
Edward Shtern
55
Claro, ¡pero la política no significa que siempre se ejecute correctamente! Pensé que valía la pena mencionarlo, al menos.
Steven Schlansker
Es cierto, pero en este caso fue ;-)
Edward Shtern
28

Hemos visto este mismo comportamiento en el pasado. Resultó que, por alguna extraña razón, si ocurría una NullPointerException en el mismo lugar en el código varias veces, después de un tiempo, el uso Log.error(String, Throwable)dejaría de incluir rastros completos de la pila.

Intenta mirar más atrás en tu registro. Puede encontrar al culpable.

EDITAR: este error suena relevante, pero se solucionó hace tanto tiempo que probablemente no sea la causa.

Matt Solnit
fuente
2
El error está cerrado, pero aún se necesita el indicador -XX: -OmitStackTraceInFastThrow para solucionar la optimización del rendimiento.
Joshua Goldberg
He estado viendo esto recientemente mucho. ¿Alguna pista sobre lo que podría estar causando esto o cómo solucionarlo? El sistema de registro puede haber sido por días, y la causa real girado hacia fuera, no importa la tediosa búsqueda ...
Pawel Veselov
55
Pawel, ¿has probado la -XX:-OmitStackTraceInFastThrowbandera JVM sugerida por Joshua? Consulte también stackoverflow.com/a/2070568/6198 .
Matt Solnit
1
Esto fue para nosotros. Gracias.
Andrew Cheong el
20

Aquí hay una explicación: Hotspot causó excepciones para perder sus rastros de pila en producción, y la solución

Lo he probado en Mac OS X

  • versión de Java "1.6.0_26"
  • Java (TM) SE Runtime Environment (compilación 1.6.0_26-b03-383-11A511)
  • Máquina virtual de servidor Java HotSpot (TM) de 64 bits (compilación 20.1-b02-383, modo mixto)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Para este fragmento específico de código, 12288 iteraciones (¿+ frecuencia?) Parece ser el límite en el que JVM ha decidido utilizar una excepción preasignada ...

Benoît Guérout
fuente
10

exception.toString no te da el StackTrace, solo devuelve

Una breve descripción de este lanzable. El resultado es la concatenación de:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

Use en su exception.printStackTracelugar para generar el StackTrace.

Peter Lang
fuente
Lo siento, hablé mal en mi publicación original. Estoy registrando estos a través de Log4J, que usa printStackTrace ().
Edward Shtern
1
¿Has intentado usar getStackTrace()para asegurarte de que el problema no está en tu registrador?
Peter Lang
1
Si está utilizando log4j, asegúrese de enviar la excepción como parte del argumento al método de registro. Publicaré una respuesta con eso.
Ravi Wallau
@raviaw punto válido! @Edward Shtern: ¿puedes confirmar que definitivamente estás usando la forma de 2 argumentos del método log4j? Sé que mencionó en una respuesta más abajo que es la política de la compañía hacerlo, pero ¿está ABSOLUTAMENTE seguro de que en este caso está siguiendo la política?
KarstenF
Puede ser una posibilidad remota, pero ¿es posible que la excepción se origine en algún código de terceros? Tal vez es un contenedor de excepciones (mal escrito), cuyo toString () simplemente devuelve el nombre de clase de la excepción envuelta, y que no puede proporcionar el seguimiento de pila subyacente. Intente poner algo como logger.info ("Exception class =" + exc.class.getCanonicalName ()) en su bloque catch y vea qué obtiene.
KarstenF
4

Sugerencia alternativa: si está utilizando Eclipse, puede establecer un punto de interrupción en NullPointerException (en la perspectiva de Depuración, vaya a la pestaña "Puntos de interrupción" y haga clic en el pequeño icono que tiene un!)

Marque las opciones "atrapado" y "no atrapado": ahora, cuando active el NPE, inmediatamente tendrá un punto de interrupción y podrá pasar y ver cómo se maneja exactamente y por qué no está obteniendo un seguimiento de la pila.

Steven Schlansker
fuente
1

toString()solo devuelve el nombre de la excepción y el mensaje opcional. Sugeriría llamar

exception.printStackTrace()

para volcar el mensaje, o si necesita los detalles sangrientos:

 StackTraceElement[] trace = exception.getStackTrace()
Sheldon Young
fuente
Ver más arriba - Escribí mal - Estoy usando printStackTrace ().
Edward Shtern
1

(Su pregunta aún no está clara sobre si su código está llamando printStackTrace()o si lo está haciendo un controlador de registro).

Aquí hay algunas explicaciones posibles sobre lo que podría estar sucediendo:

  • El registrador / manejador que se está utilizando se ha configurado para que solo muestre la cadena de mensaje de la excepción, no un seguimiento completo de la pila.

  • Su aplicación (o alguna biblioteca de terceros) está registrando la excepción utilizando en LOG.error(ex);lugar de la forma de 2 argumentos (por ejemplo) el método log4j Logger.

  • El mensaje proviene de un lugar diferente de donde crees que está; por ejemplo, en realidad se trata de algún método de biblioteca de terceros, o algunas cosas aleatorias sobrantes de intentos anteriores de depuración.

  • La excepción que se está registrando ha sobrecargado algunos métodos para ocultar el seguimiento de la pila. Si ese es el caso, la excepción no será una NullPointerException genuina, sino que será un subtipo personalizado de NPE o incluso alguna excepción no conectada.

Creo que la última explicación posible es bastante improbable, pero la gente al menos contempla hacer este tipo de cosas para "prevenir" la ingeniería inversa. Por supuesto, solo realmente logra dificultar la vida de los desarrolladores honestos.

Stephen C
fuente
1

Cuando utiliza AspectJ en su proyecto, puede suceder que algún aspecto oculte su parte del seguimiento de la pila. Por ejemplo, hoy tuve:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

Este rastro de la pila se imprimió al ejecutar la prueba a través de la prueba segura de Maven.

Por otro lado, al ejecutar la prueba en IntelliJ, se imprimió un seguimiento de pila diferente:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)
Roland Illig
fuente
0

Esto generará la excepción, use solo para depurar, debería manejar mejor sus excepciones.

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
Michael D. Irizarry
fuente