web-dev-qa-db-fra.com

La trace de pile NullPointerException n'est pas disponible sans l'agent de débogage

J'ai récemment trouvé un bogue qui provoque une exception NullPointerException. L'exception est interceptée et enregistrée à l'aide d'une instruction slf4j standard. Code abrégé ci-dessous:

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

Comme vous pouvez le voir, rien d'extraordinaire. Cependant, de toutes les instructions de journalisation des exceptions que nous avons, seule celle-ci n'imprime pas de trace de pile. Tout ce qu'il affiche est le message (représenté par "...") et le nom de la classe d'exception (Java.lang.NullPointerException).

Étant donné que la trace de pile sur une exception est chargée paresseusement, j'ai pensé qu'il y avait peut-être un problème de réorganisation des instructions et j'ai décidé d'appeler e.getStackTrace () avant l'instruction log. Cela n'a fait aucune différence.

J'ai donc décidé de redémarrer avec l'agent de débogage activé. Cependant, parce que je me suis même attaché au processus, j'ai remarqué que maintenant les traces de pile étaient en cours d'impression. Il est donc clair que la présence de l'agent de débogage a rendu disponibles des informations de débogage supplémentaires.

Depuis lors, j'ai corrigé la cause première de l'exception. Mais j'aimerais savoir pourquoi la trace de la pile n'était pas disponible sans débogueur. Quelqu'un sait?

Clarification: ce n'est pas un problème de journalisation. Imaginez la même clause try/catch, mais dans la capture, j'imprime la valeur de:

e.getStackTrace().length

Sans débogueur, cela affiche "0", avec un débogueur, il imprime un nombre positif (9 dans ce cas).

Plus d'informations: cela se produit sur JDK 1.6.0_13, 64bit, AMD64, linux 2.6.9

58
omerkudat

Est-il possible que ce code soit dans une boucle interne? Ensuite, le compilateur JIT peut être en train de compiler la pile d'appels pour cela en code natif, perdant les informations de la pile. Ensuite, lorsque vous attachez le débogueur, il désactive le JIT, rendant les informations à nouveau disponibles.

Les autres exceptions manuelles continuent d'afficher les informations car le JIT ne s'optimise pas.

Il semble que cela puisse parfois arriver pour d'autres à partir d'un commentaire dans le code source de cette classe à la ligne 102:

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

19
Nick Fortescue

Avec l'indicateur JVM -XX: -OmitStackTraceInFastThrow, vous pouvez désactiver l'optimisation des performances de la JVM pour ce cas d'utilisation. SI ce paramètre est donné, ce qui désactive l'indicateur, le stacktrace sera disponible.

Pour plus d'informations, veuillez consulter les notes de version suivantes:

"Le compilateur du serveur VM fournit désormais des traces de pile correctes pour toutes les exceptions intégrées" à froid ". À des fins de performances, lorsqu'une telle exception est levée plusieurs fois, la méthode peut être recompilée . Après recompilation, le compilateur peut choisir une tactique plus rapide en utilisant des exceptions préallouées qui ne fournissent pas de trace de pile. Pour désactiver complètement l'utilisation des exceptions préallouées, utilisez ce nouvel indicateur: -XX: -OmitStackTraceInFastThrow. " http://Java.Sun.com/j2se/1.5.0/relnotes.html

89
vesparun

Je peux reproduire cela, mais il semble assez étrange que cela se produise quelque part dans votre action.

Si vous appelez setStackTrace avec un tableau vide, cela ne fera apparaître que le texte.

 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'exécuter ....

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

% Java Fark nothing
Java.lang.NullPointerException
0
seth