在商用虛擬機中,Java程序最初是經過解釋器(Interpreter)進行解釋執行的,當虛擬機發現某個方法或者代碼塊運行特別頻繁時 就會把這些代碼認定爲「熱點代碼」,爲了提升熱點代碼的執行效率,在運行時,虛擬機將會把這些代碼編譯成與本地平臺相關的機器碼 ,完成這個任務的編譯器成爲即時編譯器(JIT Just In Time Compiler)線程
當程序須要迅速啓動和執行的時候,解釋器能夠首先發揮做用,省去編譯的時間,當即執行。server
程序運行後隨着時間的推移,編譯器逐步發揮做用,把愈來愈多的代碼編譯成本地代碼以後,能夠得到更高的執行效率。對象
編輯的「熱點代碼」 有如下兩類:編譯器
第一種是因爲方法被調用觸發的編譯,編譯器理所固然以整個方法做爲編譯對象,這種編譯也是虛擬機中標準的JIT編譯方式。對於後一種狀況 ,儘管是循環體,可是編譯器依然會以整個方法做爲編譯對象,而不是單獨的循環體。這種逼啊安逸方式由於編譯放生在方法執行過程之中,因 此被形象的成爲"棧上替換"(On Stack Replacement ,簡稱OSR編譯,即方法棧幀還在棧上,方法就被替換了)虛擬機
還有一個問題是什麼叫作屢次,屢次是指多少次?還有一個問題就是虛擬機如何統計一個方法執行的次數?解決了這個問題也就回答了即時編譯 觸發的條件。
判斷一段代碼是否是熱點代碼,是否是須要觸發即時編譯,這樣的行爲成爲熱點探測 ,其實熱點探測並不須要必定要知道方法具體被調用 了多少次,目前主要的熱點探測的方式有以下兩種:編譯
方法調用計數器,顧名思義,就是統計方法調用的次數,在client模式下,是1500次,在server模式下是10000次。這個閾值能夠經過虛擬 機參數-XX::CompileThreadhold來人爲設定。方法調用時,先檢查是否存在被JIT編譯後的版本,存在的話則優先使用編譯後的本地代碼, 不存在則將此方法計數器+1,而後判斷方法調用計數器和回邊計數器紙盒是否超過方法調用計數器的閾值。超過的話就發送及時編譯請求。效率
這個次數不是絕對的次數,而是一個相對的執行頻率,即一段時間以內方法被調用的次數,當超過必定的時間限度,若是方法的調用次數仍然不足 以讓他提交給即時編譯器編譯,那這個方法的調用計數器會被減小一半,這個過程被成爲方法調用計數器熱度衰減,而這段週期就稱爲半衰週期。 進行熱度衰減的動做是在垃圾回收時順便進行的,可使用虛擬機參數 -XX:-UseCounterDecay來關閉熱度衰減,可使用 -XX:CounterHalfLifeTime 參數設置半衰週期的時間,單位是秒。 另一種計數器——「回邊計數器」,它的做用是統計方法中循環體代碼的執行次數,在字節碼中遇到控制流向後跳轉的指令成爲「回邊」。目的 也是爲了觸發OSR編譯。與方法計數器不一樣,回邊計數器沒有計算熱度衰減的過程,所以這個計數器就是該方法循環的絕對次數cli