美文网首页JAVA进阶
Java踩知-NPE异常无堆栈

Java踩知-NPE异常无堆栈

作者: 简单即是深度 | 来源:发表于2020-04-18 23:13 被阅读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没有具体异常信息时可以尝试向前找找日志。

    相关文章

      网友评论

        本文标题:Java踩知-NPE异常无堆栈

        本文链接:https://www.haomeiwen.com/subject/ewqooctx.html