有業務反饋,線上一個應用運行了一段時間以後,在高峯期以後,忽然發現處理能力降低,接口的響應時間變長,可是看Cat上的GC數據,一切都很正常。java
經過跳板機上機器查看日誌,發現一段平時不多見到的日誌:面試
Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled. Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=. ... 「CompilerThread0」 java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?
其中CodeCache is full,說明Code Cache已經滿了,致使Compiler失效,這是爲何?算法
首先,咱們得了解什麼是Code Cache。緩存
Java代碼在執行次數達到一個閾值會觸發JIT編譯,一旦代碼塊被編譯成本地機器碼,下次執行的時候會直接運行編譯後的本地機器碼。因此這本地機器碼必須被緩存起來,而緩存這個本地機器碼的內存區域就是Code Cache,它並不屬於Java堆的一部分,除了JIT編譯的代碼以外,Java所使用的本地方法代碼(JNI)也會存在codeCache中。架構
因爲Code Cache是一塊內存區域,那麼確定有大小的限制,可是不一樣版本的JVM、不一樣的啓動方式,Code Cache的默認大小也不一樣,可經過jinfo -flag ReservedCodeCacheSize
進行查看。性能
服務啓動以後,隨着時間的推移,確定會有愈來愈多的方法被JIT編譯成本地機器碼,並存放到Code Cache,因爲Code Cache大小是固定的,那麼就存在被用完的風險。學習
一旦Code Cache被填滿,就會出現下面狀況:spa
這種狀況下,若是應用中還有不少代碼以解釋方式執行,其性能會大大下降。爲了不這種狀況,就須要對Code Cache比較深刻的理解。線程
JVM啓動的時候,Code Cache所需內存會被單獨初始化,這時候Java堆還會被初始化,因此Code Cache和Java堆是兩塊獨立內存區域。調試
在codeCache.cpp
的CodeCache::initialize()
方法中,實現了Code Cache的初始化
Code Cache包含了3種數據:
經過SegmentedCodeCache
參數能夠選擇按照總體初始化,仍是分段初始化。
經過-XX:ReservedCodeCacheSize
參數能夠指定Code Cache的初始化大小,這個默認值在不一樣的JDK版本也不一樣,目前我這邊調試的是OpenJDK11,默認大小是240M,這個已經夠用了。
能夠看下其它版本的默認大小:
對於那些只有32M、48M的就可能存在Code Cache不足的隱患,增長ReservedCodeCacheSize
能夠是一個解決方案,但這一般只是一個臨時的解決方案。
幸運的是,JVM提供了一種比較激進的codeCache回收方式:Speculative flushing。
在JDK1.7.0_4以後這種回收方式默認開啓,而以前的版本須要經過一個參數來開啓:-XX:+UseCodeCacheFlushing。
在Speculative flushing開啓的狀況下,當Code Cache不足時:
很不幸的是,在JDK1.7中,Speculative flushing釋放了一部分空間,可是從編譯日誌來看,JIT並無恢復正常,而且系統總體性能降低不少,出現了大量超時。
在Oracle官網上看到這樣一個Bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8006952 因爲算法問題,當Code Cache不足以後會致使編譯線程沒法繼續,而且消耗大量CPU,致使系統運行變慢。
以爲不錯請點贊支持,歡迎留言或進個人我的羣855801563領取【架構資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不按期答題、探討。