我最近发现了一个导致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上
答案 0 :(得分:88)
使用JVM标志-XX:-OmitStackTraceInFastThrow可以为此用例禁用JVM的性能优化。如果给出了这个禁用标志的参数,则栈跟踪将可用。
有关详细信息,请查看以下发行说明:
“服务器VM中的编译器现在为所有”冷“内置异常提供正确的堆栈回溯。出于性能目的,当抛出这样的异常几次时,可以重新编译该方法。重新编译后,编译器可以使用不提供堆栈跟踪的预分配异常选择更快的策略。要完全禁用预分配异常的使用,请使用以下新标志:-XX:-OmitStackTraceInFastThrow。“ http://java.sun.com/j2se/1.5.0/relnotes.html
答案 1 :(得分:19)
这段代码是否可能在内循环中?然后,JIT编译器可能正在将此调用堆栈编译为本机代码,从而丢失堆栈信息。然后当你附加调试器时,它会禁用JIT,使信息再次可用。
其他手动例外会在JIT未优化时继续显示信息。
在第102行的此类源代码中的注释中,其他人有时会发生这种情况:
http://logging.apache.org/log4j/1.2/xref/org/apache/log4j/spi/LocationInfo.html
答案 2 :(得分:0)
我可以复制这个但是在某个地方你的行动中会发生这种情况似乎有点奇怪。
如果使用空数组调用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