jvm——CodeCache


https://juejin.im/post/5c890f21f265da2d993dc692

CodeCache是熱點代碼的暫存區,經過即時編譯器編譯的代碼會放在這里,它存在於堆外內存。除了JIT編譯的代碼之外,Java所使用的本地方法代碼(JNI)也會存在codeCache中。

JVM內部會先嘗試解釋執行Java字節碼,當方法調用或循環回邊達到一定次數時,會觸發即時編譯,將Java字節碼編譯成本地機器碼以提高執行效率。這個編譯的本地機器碼是緩存在CodeCache中的,如果有大量的代碼觸發了即時編譯,而且沒有及時GC的話,CodeCache就會被填滿。

一旦CodeCache被填滿,已經被編譯的代碼還會以本地代碼方式執行,但后面沒有編譯的代碼只能以解釋執行的方式運行。

通過第2小節的比較,可以清晰看出解釋執行和編譯執行的性能差異。所以對於大多數應用來說,這種情況的出現是災難性的。

JVM針對CodeCache提供了GC方式: -XX:+UseCodeCacheFlushing。在JDK1.7.0_4之后這個參數默認開啟,當CodeCache即將填滿時會嘗試回收。JDK7在這方面的回收做的不是很少,GC收益較低,在JDK8有了很大的改善,所以可以通過升級到JDK8來直接提升這方面的性能。
在Java8中提供了一個JVM啟動參數:-XX:+PrintCodeCache,他可以在JVM停止時打印CodeCache的使用情況,可以在每次停止應用時觀察一下這個值,慢慢調整為一個最合適的大小。

通過在啟動參數上增加:-XX:+UseCodeCacheFlushing 來啟用;

打開這個選項,在JIT被關閉之前,也就是CodeCache裝滿之前,會在JIT關閉前做一次清理,刪除一些CodeCache的代碼

如果清理后還是沒有空間,那么JIT依然會關閉。這個選項默認是關閉的;

在JDK8中,當以server模式啟動時,分層編譯默認開啟。需要注意的是, 分層編譯方式只能用於server模式中,如果需要關閉分層編譯,需要加上啟動參數 -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編譯還在正常執行,系統運行速度也沒有收到影響。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM