Java踩知-NPE異常無堆棧

1、背景

在生產(chǎn)環(huán)境中,開發(fā)人員多少都在生產(chǎn)環(huán)境中遇到過NPE異常明明printStackTrace 了,堆棧信息只有令人絕望的的”null“ 4個(gè)字母。但其實(shí)往前翻一番日志,還是會(huì)找到包含具體NPE 位置的堆棧信息,但是為什么有時(shí)候有NPE位置信息,有時(shí)候沒有呢?

又有沒有解決的必要性呢?

2、原因、方案

簡(jiǎn)書:http://www.itdecent.cn/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針對(duì)頻繁出現(xiàn)的異常做了優(yōu)化,可以在出現(xiàn)異常的時(shí)候快速拋出,不需要打印出整個(gè)調(diào)用鏈,這樣可以節(jié)省異常堆棧的內(nèi)存分配。

2.2、解決方案

既然jvm針對(duì)這個(gè)做了優(yōu)化,那肯定有禁用這個(gè)優(yōu)化的方法,那就是-XX:-OmitStackTraceInFastThrow參數(shù)(禁用快拋,即展示完整異常)

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、測(cè)試主類

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、運(yùn)行5次結(jié)果

count:6865
count:6913
count:6862
count:6889
count:6885

3.3、結(jié)論

jvm優(yōu)化,不打印異常堆棧信息的次數(shù)是多少呢?

筆者環(huán)境:jdk1.8.0_77
測(cè)試結(jié)果為6800-7000 之間

對(duì)于線上環(huán)境,這個(gè)數(shù)值還是挺容易達(dá)到的。

4、處理建議

雖然使用-XX:-OmitStackTraceInFastThrow 可以禁用jvm 優(yōu)化,每次異常都會(huì)打印完整堆棧信息,方便直接定位異常位置。但是筆者還是建議保持這個(gè)優(yōu)化,因?yàn)榇蛴《褩P畔⑿枰闅v整個(gè)線程堆棧,是比較耗費(fèi)性能的。

當(dāng)線上發(fā)現(xiàn)NPE沒有具體異常信息時(shí)可以嘗試向前找找日志。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、編程規(guī)約 (一)命名規(guī)約 【強(qiáng)制】 代碼中的命名均不能以下劃線或美元符號(hào)開始,也不能以下劃線或美元符號(hào)結(jié)束。反...
    喝咖啡的螞蟻閱讀 1,603評(píng)論 0 2
  • 阿里巴巴 JAVA 開發(fā)手冊(cè) 1 / 32 Java 開發(fā)手冊(cè) 版本號(hào) 制定團(tuán)隊(duì) 更新日期 備 注 1.0.0 阿...
    糖寶_閱讀 7,882評(píng)論 0 5
  • 在 Java 中處理異常并不是一個(gè)簡(jiǎn)單的事情。不僅僅初學(xué)者很難理解,即使一些有經(jīng)驗(yàn)的開發(fā)者也需要花費(fèi)很多時(shí)間來思考...
    JourWon閱讀 1,032評(píng)論 0 2
  • 命名風(fēng)格 【強(qiáng)制】代碼中的命名均不能以下劃線或美元符號(hào)開始,也不能以下劃線或美元符號(hào)結(jié)束 【強(qiáng)制】代碼中的命名嚴(yán)禁...
    云A00000閱讀 4,043評(píng)論 0 0
  • 養(yǎng)生不排寒,一切都白談! 萬病由寒起,寒從腳根生! 樹老根先死,人老腳先衰! 女人不排寒,定有小肚腩! 女人不排寒...
    小兒推拿羅清紅閱讀 314評(píng)論 0 0

友情鏈接更多精彩內(nèi)容