今天說的異常是一個很不常見的異常,至少我不經常見到這個異常。
首先先看下NoClassDefFoundError官方定義 :
Java Virtual Machine is not able to find a particular class at runtime which was available at compile time. If a class was present during compile time but not available in java classpath during runtime.
Java 虛擬機無法在運行時找到一個在編譯時可用的特定類。如果在編譯時存在類, 但在運行時 java 類路徑中不可用。
最近做的一個項目,由同事到客戶方部署及應用,但是期間發生一個詭異的問題:同一套代碼打出的jar包在一個公司運行時會有一個NoClassDefFoundError異常拋出。起初看到這個異常,我們都認為是打得包或者依賴有問題。於是便重新打包部署,結果還是同樣的問題。異常信息如下:
很詭異的問題,順着報的錯誤去繼續查找原因,最后將問題定位到一個線程池工具類中,部分代碼如下:
其中 DEFAULT_MAX_CONCURRENT 定義如下:
private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;
這個線程池工具類在本地以及測試環境和線上環境一直都運行的沒有問題,因為報錯的異常信息指向了這個類。
考慮到在多個客戶部署的都是同一套代碼,只有硬件配置可能不同,而我們線程池初始化時的核心線程數依賴於硬件CPU核數,所以便猜測初始化線程池出了問題,核心線程數可能比最大線程數還大。
於是便開始追蹤源碼,一探究竟。
線程池初始化源碼:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
繼續往下看其初始化過程:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
上面代碼可能看出,如果corePoolSize>maxPoolSize 則會拋出:IllegalArgumentException 異常,但是這和我們問題壓根不一樣啊?線索到這里就斷了,但是至少發現了代碼的一處Bug。
於是又開始沉思這個NoClassDefFoundError 異常究竟是怎么來的了,打開Oracle 文檔便開始全局搜索這個,果不其然,有了新的發現:
(文檔地址:https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2)
這里意思是初始化過程時,如果這個類是用c去實現的,且初始化拋出異常時,都會對外拋出NoClassDefFoundError 異常,到了這里就很明朗了,果然是初始化線程池搞錯了。
於是趕緊查看客戶機器CPU核數來驗證自己的猜想,果不其然,CPU為8核處理器。趕緊改了代碼重新打包部署,一切到這里就結束了。
不過通過這次異常也學到了很多:
1,能不用硬編碼的應該堅決杜絕,少埋這種坑。
2,多查文檔,多查官方文檔。
由於博主能力有限,所以如果您有更多的見解還請留言告知,不勝感激。