NullPointerException 堆栈跟踪在没有调试代理的情况下不可用

Posted

技术标签:

【中文标题】NullPointerException 堆栈跟踪在没有调试代理的情况下不可用【英文标题】:NullPointerException stack trace not available without debug agent 【发布时间】:2010-11-07 18:03:02 【问题描述】:

我最近发现了一个导致 NullPointerException 的错误。使用标准 slf4j 语句捕获并记录异常。代码如下:

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

如您所见,没什么特别的。但是,在我们拥有的所有异常日志语句中,只有这一条不会打印堆栈跟踪。它只打印消息(表示为“...”)和异常类的名称(java.lang.NullPointerException)。

由于异常的堆栈跟踪是延迟加载的,我认为可能存在某种指令重新排序问题,并决定在日志语句之前调用 e.getStackTrace()。这没什么区别。

所以我决定在启用调试代理的情况下重新启动。但是,因为我什至附加到进程,我注意到现在堆栈跟踪正在打印。很明显,调试代理的存在导致一些额外的调试信息可用。

从那时起,我已经修复了异常的根本原因。但是我想了解为什么没有调试器就无法使用堆栈跟踪。有人知道吗?

澄清:这不是日志记录问题。想象一下同样的 try/catch 子句,但是在 catch 中,我打印了以下值:

e.getStackTrace().length

如果没有调试器,它会打印“0”,如果有调试器,它会打印一个正数(在本例中为 9)。

更多信息:这发生在 JDK 1.6.0_13、64bit、amd64、linux 2.6.9 上

【问题讨论】:

你用的是什么虚拟机?这种行为听起来非常很奇怪。 JDK 1.6.0_13,Linux 2.6.9 上的 64 位 当你自己在 try 中“抛出新的 NullPointerException()”会发生什么? 尝试记录这些动作,这样你就可以知道是哪个动作导致了这个 我已经解决了根本问题。我只想解释一下这种行为。 【参考方案1】:

使用 JVM 标志 -XX:-OmitStackTraceInFastThrow,您可以针对此用例禁用 JVM 的性能优化。如果给出了这个参数,它会禁用标志,堆栈跟踪将可用。

有关更多信息,请查看以下发行说明:

“服务器VM中的编译器现在为所有“冷”内置异常提供正确的堆栈回溯。出于性能目的,当此类异常被抛出几次时,该方法可能会重新编译。重新编译后,编译器可以使用不提供堆栈跟踪的预分配异常来选择更快的策略。要完全禁用预分配异常,请使用这个新标志:-XX:-OmitStackTraceInFastThrow。 http://java.sun.com/j2se/1.5.0/relnotes.html

【讨论】:

我们又遇到了同样的问题并尝试了这个选项,效果非常好,比完全关闭 JIT 要好得多。谢谢!【参考方案2】:

这段代码有可能在内部循环中吗?然后,JIT 编译器可能会将调用堆栈编译为本机代码,从而丢失堆栈信息。然后,当您附加调试器时,它会禁用 JIT,使信息再次可用。

由于 JIT 未优化,其他手动异常会继续显示信息。

从第 102 行此类源代码中的注释看来,其他人有时会发生这种情况:

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

【讨论】:

后来我设法确认关闭 JIT (JAVA_COMPILER=false) 会导致在发生此异常时可以使用完整的堆栈跟踪。【参考方案3】:

我可以复制这一点,但在你的 Action 某处发生这种情况似乎有点奇怪。

如果使用空数组调用 setStackTrace,则只会显示文本。

 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;
     
 

运行它....

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

% java Fark nothing
java.lang.NullPointerException

【讨论】:

是的,有人也提到了这一点。事实并非如此。 如果你自己在 try 中抛出 NullPointerException 也会发生同样的事情吗? 是的,如果我自己手动投掷,行为相同。这只发生在这个 try/catch 中。在我手动投掷的其他情况下,我得到正常(即全栈)行为。

以上是关于NullPointerException 堆栈跟踪在没有调试代理的情况下不可用的主要内容,如果未能解决你的问题,请参考以下文章

handleStopActivity 中的 NullPointerException -- 在堆栈跟踪中没有引用我的代码

Google Play 控制台堆栈跟踪

J2ME 混淆的应用程序堆栈跟踪

EntityManager NullPointerException

Struts2 ExecAndWait NullPointerException

Unmarshal NullPointerException(带有 JAXB 参考实现)