上海這邊的網際網路公司大部分是2-7号在家遠端辦公,有些可能更晚,這期間工作上的事情不多,就把《深入了解java虛拟機》這本書又撸了一遍,順便寫下心得體會,和大家分享,溫故知新。
一. codeCache簡介
從字面意思了解就是代碼緩存區,它緩存的是JIT(Just in Time)編譯器編譯的代碼,簡言之codeCache是存放JIT生成的機器碼(native code)。當然JNI(Java本地接口)的機器碼也放在codeCache裡,不過JIT編譯生成的native code占主要部分。
大緻在JVM中的分布如下:

大家都知道javac編譯器,把java代碼編譯成class位元組碼,它和JIT編譯器的差別是,javac隻是前端編譯(有的叫前期編譯),jvm是通過執行機器碼和底層互動的,這樣我們編寫的業務代碼才能生效。是以還要把位元組碼class編譯成與本地平台相關的機器碼,這個過程就是後端編譯。
後端編譯根據具體的執行方式不同又分為兩種:
1.解釋執行
一行一行解釋成機器碼再執行,每次調用時都需要重新逐條解釋執行。
2.編譯執行(JIT)
将頻繁調用的方法或循環體編譯成機器碼後,進行多層優化,然後緩存到codeCache裡,避免重複編譯。
兩種執行方式的差別很明顯,第一種在遇到頻繁調用的方法或代碼塊時執行效率很低,但是解釋執行可以節省記憶體(不存放到codeCache),立即執行。然後當程式運作一段時間後(達到一定的編譯次數),編譯執行即JIT優化,可以獲得更高的執行效率。
是以說二者是相輔相成的。
現在的Java虛拟機這兩種方式都包含(通過指令行java -version檢視):
其實JIT編譯隻是一個統稱,具體要看jvm是client端還是server端的,不同的端會分為C1,C2編譯器,這兩種編譯器的差別下一篇會講到,這裡先不展開。
二. JIT編譯優化
上面講到了JVM會對頻繁使用的代碼,即熱點代碼(Hot Spot Code),達到一定的門檻值後會編譯成本地平台相關的機器碼,并進行各層次的優化,提升執行效率。
熱點代碼也分兩種:
被多次調用的方法
被多次執行的循環體
那門檻值如何判斷呢?
1.方法計數器,統計被多次調用的方法次數,該計數器統計的并不是方法被調用的絕對次數,而是在一段時間内方法被調用的次數。server模式下預設是10000次,可以通過-XX:CompileThreshold來設定(client模式一般很少用到,預設是1500)。
2.回邊計數器,統計一個方法中循環體代碼執行的絕對次數,在位元組碼中遇到控制流向後跳轉的指令稱為回邊,主要通過OnStackReplacePercentage設定。
編譯後進行優化,JIT的優化有很多種,比如:
針對方法的優化,方法内聯(可以參考上一篇文章Java開發規範之性能篇,關于inline的解釋)
針對多次調用的循環體優化:棧上替換OSR(On-Stack Replace)
無用代碼消除
複寫傳播
逃逸分析
更多JIT優化技術可參考jvm官網介紹
三. codeCache注意事項
上面主要講了codeCache的作用和JIT的關系,codeCache主要是存放JIT編譯後的機器代碼,codeCache的大小主要是通過下面的參數設定:
-XX:InitialCodeCacheSize
設定codeCache初始大小,一般預設是48M
-XX:ReservedCodeCacheSize
設定codeCache預留的大小,通常預設是240M
如果codeCache的記憶體滿了會進行回收,但在jdk1.8之前的jvm回收算法有點問題,當codeCache滿了之後會導緻編譯線程無法繼續,并且消耗大量CPU導緻系統運作變慢,現象就是系統響應增加,如果你也遇到這個問題建議直接更新成jdk8,或者調大codeCache記憶體。
codeCache的大小設定可以通過-XX:+PrintCodeCache參數檢視調整,但這個參數隻在JVM停止的時候列印codeCache使用情況,是以如果想實時監控codeCache的使用情況,可以參考如下代碼:
運作後可以檢視contents結果
可以看到我本地的codeCahe配置,初始化是2555904,最大為251658240,已使用2395648
以上是我的一些了解,今天就寫到這裡,下次見 ^_^
END -