1、背景
在生产环境中,开发人员多少都在生产环境中遇到过NPE异常明明printStackTrace 了,堆栈信息只有令人绝望的的”null“ 4个字母。但其实往前翻一番日志,还是会找到包含具体NPE 位置的堆栈信息,但是为什么有时候有NPE位置信息,有时候没有呢?
又有没有解决的必要性呢?
2、原因、方案
简书:https://www.jianshu.com/p/03d41fb71987
Stack Overflow:https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace
We have seen this same behavior in the past.It turned out that, for some crazy reason,if a NullPointerException occurred at the same place in the code multiple times, after a while using Log.error(String, Throwable) would stop including full stack traces.
Try looking further back in your log. You may find the culprit.
2.1、原因
jvm针对频繁出现的异常做了优化,可以在出现异常的时候快速抛出,不需要打印出整个调用链,这样可以节省异常堆栈的内存分配。
2.2、解决方案
既然jvm针对这个做了优化,那肯定有禁用这个优化的方法,那就是-XX:-OmitStackTraceInFastThrow参数(禁用快抛,即展示完整异常)
3、多少次异常后不打印具体异常信息?
3.1、异常类
public class WithNPE extends Thread {
private int count;
public WithNPE(Integer count) {
this.count = count;
}
@Override
public void run() {
try {
String str = null;
// 制造空指针NPE
System.out.println(str.length());
} catch (Throwable e) {
if (e.getStackTrace().length == 0) {
FastThrowMain.countDown();
System.out.println();
System.out.println();
System.out.printf("count:" + count);
}
}
}
}
3.2、测试主类
public class FastThrowMain {
private static volatile AtomicInteger count = new AtomicInteger(0);
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
for (int i = 0; i < 8000; i++) {
WithNPE withNPE = new WithNPE(count.getAndIncrement());
executorService.execute(withNPE);
}
countDownLatch.await();
executorService.shutdown();
}
public static void countDown() {
countDownLatch.countDown();
}
}
3.2、运行5次结果
count:6865
count:6913
count:6862
count:6889
count:6885
3.3、结论
jvm优化,不打印异常堆栈信息的次数是多少呢?
笔者环境:jdk1.8.0_77
测试结果为6800-7000 之间
对于线上环境,这个数值还是挺容易达到的。
4、处理建议
虽然使用-XX:-OmitStackTraceInFastThrow 可以禁用jvm 优化,每次异常都会打印完整堆栈信息,方便直接定位异常位置。但是笔者还是建议保持这个优化,因为打印堆栈信息需要遍历整个线程堆栈,是比较耗费性能的。
当线上发现NPE没有具体异常信息时可以尝试向前找找日志。