https://juejin.im/post/5c890f21f265da2d993dc692
CodeCache是熱點代碼的暫存區,經過即時編譯器編譯的代碼會放在這里,它存在於堆外內存。除了JIT編譯的代碼之外,Java所使用的本地方法代碼(JNI)也會存在codeCache中。
JVM內部會先嘗試解釋執行Java字節碼,當方法調用或循環回邊達到一定次數時,會觸發即時編譯,將Java字節碼編譯成本地機器碼以提高執行效率。這個編譯的本地機器碼是緩存在CodeCache中的,如果有大量的代碼觸發了即時編譯,而且沒有及時GC的話,CodeCache就會被填滿。
一旦CodeCache被填滿,已經被編譯的代碼還會以本地代碼方式執行,但后面沒有編譯的代碼只能以解釋執行的方式運行。
通過第2小節的比較,可以清晰看出解釋執行和編譯執行的性能差異。所以對於大多數應用來說,這種情況的出現是災難性的。
通過在啟動參數上增加:-XX:+UseCodeCacheFlushing 來啟用;
打開這個選項,在JIT被關閉之前,也就是CodeCache裝滿之前,會在JIT關閉前做一次清理,刪除一些CodeCache的代碼;
如果清理后還是沒有空間,那么JIT依然會關閉。這個選項默認是關閉的;
-XX:-TieredCompilation
;如果以client模式啟動,
-XX:+TieredCompilation
參數將會被忽略。
JIT編譯器被停止了,並且不會被重新啟動,此時會回歸到解釋執行;
被編譯過的代碼仍然以編譯方式執行,但是尚未被編譯的代碼就 只能以解釋方式執行了。
針對這種情況,JVM提供了一種比較激進的codeCache回收方式:Speculative flushing
。
在JDK1.7.0_4之后這種回收方式默認開啟,而之前的版本需要通過一個啟動參數來開啟:-XX:+UseCodeCacheFlushing。
在Speculative flushing開啟的情況下,當codeCache將要耗盡時:
最早被編譯的一半方法將會被放到一個old列表中等待回收;
在一定時間間隔內,如果old列表中方法沒有被調用,這個方法就會被從codeCache充清除;
Code Cache滿了時緊急進行清掃工作,它會丟棄一半老的編譯代碼;
Code Cache空間降了一半,方法編譯工作仍然可能不會重啟;
flushing可能導致高的cpu使用,從而影響性能下降;
JDK 8對codeCache的回收有了很明顯的改善。不僅codeCache的增長比較平緩,而且當使用量達到75%時,回收力度明顯加大,codeCache使用量在這個值上下浮動,並緩慢增長。最重要的是,JIT編譯還在正常執行,系統運行速度也沒有收到影響。