Java踩知-NPE异常无堆栈

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

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一、编程规约 (一)命名规约 【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。反...
    喝咖啡的蚂蚁阅读 5,429评论 0 2
  • 阿里巴巴 JAVA 开发手册 1 / 32 Java 开发手册 版本号 制定团队 更新日期 备 注 1.0.0 阿...
    糖宝_阅读 12,301评论 0 5
  • 在 Java 中处理异常并不是一个简单的事情。不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考...
    JourWon阅读 4,535评论 0 2
  • 命名风格 【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束 【强制】代码中的命名严禁...
    云A00000阅读 9,266评论 0 0
  • 养生不排寒,一切都白谈! 万病由寒起,寒从脚根生! 树老根先死,人老脚先衰! 女人不排寒,定有小肚腩! 女人不排寒...
    小儿推拿罗清红阅读 2,241评论 0 0

友情链接更多精彩内容