目前主流的兩款商用Java虛擬機(HotSpot、OpenJ9)里,Java程序最初都是通過解釋器(Interpreter)進行解釋執行的,當虛擬機發現某個方法或代碼塊的運行特別頻繁,就會把這些代碼認定為“熱點代碼”(Hot Spot Code),為了提高熱點代碼的執行效率,在運行時,虛擬機將會把這些代碼編譯成本地機器碼,并以各種手段盡可能地進行代碼優化,運行時完成這個任務的后端編譯器被稱為即時編譯器
### JIT編譯器
即時編譯(Just-in-time Compilation,JIT)是一種通過在運行時將字節碼翻譯為機器碼,從而改善字節碼編譯語言性能的技術。在HotSpot實現中有多種選擇:C1、C2和C1+C2,分別對應client、server和分層編譯。
1. C1編譯速度快,優化方式比較保守;
2. C2編譯速度慢,優化方式比較激進;
3. C1+C2在開始階段采用C1編譯,當代碼運行到一定熱度之后采用C2重新編譯;
解釋器與編譯器搭配使用的方式在虛擬機中被稱為“混合模式”(Mixed Mode),用戶也可以使用參數“-Xint”強制虛擬機運行于“解釋模式”(Interpreted Mode),這時候編譯器完全不介入工作,全部代碼都使用解釋方式執行。另外,也可以使用參數“-Xcomp”強制虛擬機運行于“編譯模式”(Compiled Mode),這時候將優先采用編譯方式執行程序,但是解釋器仍然要在編譯無法進行的情況下介入執行過程
### JIT參數配置
TieredCompilation:表示是否開啟JVM分層編譯(JDK8+默認開啟,JDK8以前的版本默認關閉)
```
-XX:+TieredCompilation --開啟
-XX:-TieredCompilation --關閉
```
### 編譯閾值
即時編譯JIT只在代碼段執行足夠次數才會進行優化,在執行過程中不斷收集各種數據,作為優化的決策,所以在優化完成之前,例子中的User對象還是在堆上進行分配。
那么一段代碼需要執行多少次才會觸發JIT優化呢?通常這個值由-XX:CompileThreshold參數進行設置:
1. 使用client編譯器時,默認為1500;
2. 使用server編譯器時,默認為10000;
意味著如果方法調用次數或循環次數達到這個閾值就會觸發標準編譯,更改CompileThreshold標志的值,將使編譯器提早(或延遲)編譯。
除了標準編譯,還有一個叫做OSR(On Stack Replacement)棧上替換的編譯,如上述例子中的main方法,只執行一次,遠遠達不到閾值,但是方法體中執行了多次循環,OSR編譯就是只編譯該循環代碼,然后將其替換,下次循環時就執行編譯好的代碼,不過觸發OSR編譯也需要一個閾值,可以通過以下公式得到。
```
-XX:CompileThreshold = 10000
-XX:OnStackReplacePercentage = 140
-XX:InterpreterProfilePercentage = 33
OSR trigger = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100 = 10700
```
其中trigger即為OSR編譯的閾值
參考資料
[https://www.jianshu.com/p/20bd2e9b1f03](https://www.jianshu.com/p/20bd2e9b1f03)
http://www.infoq.com/cn/articles/OpenJDK-HotSpot-What-the-JIT
- 前言
- Write once, run anywhere
- 概述
- JAVA虛擬機
- JVM整體結構
- JVM架構模型
- JVM虛擬機分類
- HotSpot VM
- JRockit
- IBM-J9
- Azul/zing VM
- Taobao VM
- Dalvik VM
- Graal VM
- JAVA源碼編譯機制
- Javac編譯器
- 分析和輸入到符號表
- 注解處理
- 語義分析和生成class文件
- ECJ編譯器
- 類執行機制
- 字節碼解釋執行
- 棧頂緩存
- 部分棧幀共享
- 編譯執行
- 即時編譯器
- C1 Compiler
- C2 Compiler
- Graal編譯器
- C1與C2編譯器
- AOT
- 編譯優化
- 字符串優化
- 方法內聯
- 逃逸分析
- 同步消除
- 標量替換
- 棧上分配
- 去虛擬化/逆優化
- 多層編譯
- JVM編譯策略
- OSR編譯
- 冗余削除
- CodeCache
- 常量編譯優化
- JVM運行時數據區
- 程序計數器
- JAVA虛擬機棧
- 棧幀
- 局部變量表
- 操作數棧
- 本地方法棧
- Java調用native方法
- JVM Stacks && Native Stacks
- 堆-Heap
- 方法區(Method Area)
- 運行時常量池
- 常量傳播優化
- MetaSpace
- 直接內存
- StackOverflowError
- 遞歸方法
- OutOfMemoryError
- 本地內存溢出
- 執行引擎
- 運行時數據區關聯關系
- jdk8內存結構
- JMM內存模型
- JAVA內存模型
- JMM八種操作指令
- 內存屏障
- 指令重排
- as-if-serial語義
- Happen-Before規則
- 數據依賴性
- 原子性、可見性與有序性
- 偽共享
- CPU三級緩存
- 緩存行
- MESI協議
- Java中的偽共享
- ConcurrentHashMap偽共享解決方案
- 虛擬機對象
- 對象創建原理
- 對象內存布局
- 對象頭
- 實例數據
- 對象的訪問定位
- 垃圾收集器與內存分配策略
- GC相關概念
- TLAB
- JVM GC工作原理
- 內存管理
- JAVA引用分類
- 死亡標記
- 回收方法區
- 三色標記算法
- 垃圾收集算法
- 標記-清除算法
- 標記-整理算法
- 復制算法
- 分代收集算法
- HotSpot算法實現
- STW
- 垃圾收集器
- 常見的垃圾收集器
- 垃圾收集器分類
- Serial收集器
- Serial Old收集器
- ParNew收集器
- Parallel Scavenge收集器
- Parallel Old收集器
- CMS收集器
- CMS完整收集過程
- Card Table
- G1收集器
- 分代收集
- 空間整合
- 可預測的停頓時間模型
- G1&CMS
- 主要參數說明
- G1適用場景
- Remembered Set
- G1垃圾回收的過程
- G1優化建議
- Shenandoah
- ZGC
- 垃圾收集器特點
- GC日志
- GC策略的評價指標
- jvm card table數據結構
- 對象生存軌跡
- 類文件結構
- 魔數
- 版本號
- 常量池
- 訪問標志
- 父類索引
- 接口集合
- 字段集合
- 方法集合
- 屬性集合
- 類加載機制與類的初始化
- Java代碼執行流程
- 類加載過程
- 抽象類ClassLoader
- 常見類加載器
- BootstrapClassLoader
- 自定義類加載器
- 線程上下文類加載器
- 雙親委派模型
- Tomcat類加載機制
- ServiceLoader
- 類的初始化
- 常見的JVM類加載異常
- ClassNotFoundException
- NoClassDefFoundError
- LinkageError
- ClassCastException
- 虛擬機性能調優監控與故障處理工具
- CPU利用率高/飆升
- 排查及解決方案
- 上下文切換
- GC問題定位解決方案
- prommotion failed
- FullGC頻繁
- youngGC
- 內存問題
- 內存溢出和內存泄漏
- 內存溢出
- 棧溢出
- 堆溢出
- 對外內存溢出
- 內存泄漏
- 磁盤問題
- 線上問題解決方案
- 不定期出現的接口耗時現象
- 線程池異常
- 死鎖問題
- JVM調優
- jvm參考配置
- jvm-jstat
- jvm-jmap
- jvm-jstack
- jinfo
- jps
- 虛擬機的退出
- Shutdown Hook
- JVM指令
- 附錄
- 常用JVM指令
- Class文件版本號
- Class文件格式
- 方法訪問標識
- jvm常量池
- 類或接口的訪問標識
- 描述符標識字符含義
- 字段訪問標識
- Java程序與Docker容器環境
- 基準測試