事情是这样的,开开心心的在开发环境跑着定时任务,就上个厕所的功夫,回来就发现报错了。
然后错误日志是这样的:
1
2
3
4
5
6
7
8
9
10
|
Exception in thread "pool-1-thread-40699" java.lang.NullPointerException
Exception in thread "pool-1-thread-40681" java.lang.NullPointerException
Exception in thread "pool-1-thread-40704" java.lang.NullPointerException
Exception in thread "pool-1-thread-40688" java.lang.NullPointerException
Exception in thread "pool-1-thread-40691" java.lang.NullPointerException
Exception in thread "pool-1-thread-40692" java.lang.NullPointerException
Exception in thread "pool-1-thread-40719" java.lang.NullPointerException
Exception in thread "pool-1-thread-40713" java.lang.NullPointerException
Exception in thread "pool-1-thread-40715" java.lang.NullPointerException
Exception in thread "pool-1-thread-40722" java.lang.NullPointerException
|

我的堆栈信息呢?虽然平时最不想看到的就是报错,但是这次只有这么点是几个意思?
当然这难不倒我,只要我足够细心,仔仔细细的排查一下这个2000多行的类文件,就可以发现问题!
开个玩笑。。。
实际上,这是JVM的一个优化,当某处代码高频率的抛出同一异常时,为了节约性能,JVM不会再打印完整异常堆栈信息。
这个优化是默认开启的,当然也可以关闭。
只需要在启动时,加上如下参数即可:
1
|
-XX:-OmitStackTraceInFastThrow //(减号表示关闭,加号则表示启用)
|
下面是官方给出的解释:
https://www.oracle.com/java/technologies/javase/release-notes-introduction.html#vm
The compiler in the server VM now provides correct stack backtraces for all “cold” built-in exceptions. For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace. To disable completely the use of preallocated exceptions, use this new flag: .-XX:-OmitStackTraceInFastThrow
下面给出伪代码复现一下:
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for (int j = 0; j < 200000; j++) {
threadPool.execute(() -> {
String str = null;
str.toString();
});
}
}
|
执行之后,完整的堆栈信息很快就不会打印了。
这个情况是发生在开发环境,如果是在线上环境,我这边有两个解决思路:
1、在开发环境复现
2、翻线上日志,找到有完整堆栈信息的日志。
如非必要,不要在生产环境关闭这个优化。
好了,今天就到这里,我们下期再见~
