2009-07-02 5 views
55

Recentemente ho trovato un bug che causa una NullPointerException. L'eccezione viene rilevata e registrata utilizzando un'istruzione standard slf4j. Codice abbreviato di seguito:NullPointerException trace trace non disponibile senza agente di debug

for(Action action : actions.getActions()) { 
    try { 
     context = action.execute(context); 
    } catch (Exception e) { 
     logger.error("...", e); 
     break; 
    } 
} 

Come potete vedere, niente di speciale. Tuttavia, di tutte le dichiarazioni di registrazione delle eccezioni che abbiamo, solo questa non stampa una traccia dello stack. Tutto ciò che stampa è il messaggio (rappresentato come "...") e il nome della classe di eccezione (java.lang.NullPointerException).

Poiché la traccia dello stack su un'eccezione è lazy loaded, ho pensato che forse c'è un problema di riordino delle istruzioni di qualche tipo e ho deciso di chiamare e.getStackTrace() prima dell'istruzione del registro. Questo non ha fatto differenza.

Quindi ho deciso di riavviare con l'agente di debug abilitato. Tuttavia, poiché mi sono persino collegato al processo, ho notato che ora le tracce dello stack stavano stampando. Quindi, chiaramente la presenza dell'agente di debug ha reso disponibili alcune informazioni di debug aggiuntive.

Da allora ho risolto la causa principale dell'eccezione. Ma vorrei sapere perché la traccia dello stack non era disponibile senza un debugger. Qualcuno sa?

Chiarimento: questo non è un problema di registrazione. Immaginate la stessa clausola try/catch, ma nella cattura, ho stampare il valore di:

e.getStackTrace().length 

Senza un debugger questa stampe '0', con un debugger viene stampato un numero positivo (9 in questo caso).

Maggiori informazioni: questo sta accadendo su JDK 1.6.0_13, 64bit, AMD64, Linux 2.6.9

+2

che vm stai usando? quel comportamento suona * molto * strano. –

+0

JDK 1.6.0_13, 64bit su linux 2.6.9 – omerkudat

+0

Cosa succede quando si lancia il nuovo NullPointerException() 'all'interno del tentativo? – seth

risposta

18

E 'possibile che questo codice è in un ciclo interno? Quindi il compilatore JIT potrebbe compilare lo stack di chiamate per questo codice nativo, perdendo le informazioni sullo stack. Quindi quando si collega il debugger si disabilita il JIT, rendendo nuovamente disponibili le informazioni.

Le altre eccezioni manuali continuano a visualizzare le informazioni poiché il JIT non sta ottimizzando.

Sembra che questo a volte può accadere per gli altri da un commento in questo codice sorgente della classe alla riga 102:

http://logging.apache.org/log4j/1.2/xref/org/apache/log4j/spi/LocationInfo.html

+3

Più tardi sono riuscito a confermare che disattivando JIT (JAVA_COMPILER = false) si rende disponibile la traccia dello stack completo ogni volta che si verifica questa eccezione. – omerkudat

0

posso replicare questo, ma sembra un po 'strano che questo sarebbe successo nella vostra azione da qualche parte.

Se si chiama setStackTrace con un array vuoto, verrà visualizzato solo il testo.

public class Fark { 
    public static void main(String[] args) { 
     try { 
      Fark.throwMe(args.length != 0); 

     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 

    public static final void throwMe(boolean arg) throws Exception{ 
     Exception e = new NullPointerException(); 
     if (arg) { 
      e.setStackTrace(new StackTraceElement[0]); 
     } 
     throw e; 
    } 
} 

L'esecuzione ....

% java Fark 
java.lang.NullPointerException 
     at Fark.throwMe(Fark.java:15) 
     at Fark.main(Fark.java:5) 

% java Fark nothing 
java.lang.NullPointerException 
+0

Sì, qualcuno ha menzionato anche questo. Questo non è il caso. – omerkudat

+0

Succede la stessa cosa se butti tu stesso una NullPointerException nel tentativo? – seth

+0

Sì, lo stesso comportamento se lancio manualmente me stesso. E questo succede solo in questo try/catch. In altri dove lancio manualmente, ottengo un comportamento normale (cioè a pila intera). – omerkudat

83

Con la JVM bandiera XX: -OmitStackTraceInFastThrow è possibile disattivare l'ottimizzazione delle prestazioni di la JVM per questo caso d'uso. Se questo parametro è dato, che disabilita il flag, lo stacktrace sarà disponibile.

Per ulteriori informazioni si prega di dare un'occhiata alle seguenti note di rilascio:

"Il compilatore nel server VM fornisce ora backtrace dello stack corrette per tutti 'a freddo' eccezioni built-in per il calcolo delle prestazioni, quando un tale. l'eccezione viene lanciata alcune volte, il metodo può essere ricompilato Dopo la ricompilazione, il compilatore può scegliere una tattica più veloce utilizzando eccezioni preallocate che non forniscono una traccia dello stack. Per disabilitare completamente l'utilizzo delle eccezioni preallocate, utilizzare questo nuovo flag: -XX : -OmitStackTraceInFastThrow ". http://java.sun.com/j2se/1.5.0/relnotes.html

+0

Questo è molto interessante, grazie per averlo fatto notare. – omerkudat

+3

Abbiamo avuto di nuovo lo stesso problema e abbiamo provato questa opzione, e ha funzionato molto bene, molto meglio che disattivare completamente il JIT. Grazie! – omerkudat

+0

Grazie per aver segnalato questo! – Daniel