JAVA进阶

Java踩知-NPE异常无堆栈

2020-04-18  本文已影响0人  简单即是深度

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没有具体异常信息时可以尝试向前找找日志。

上一篇 下一篇

猜你喜欢

热点阅读