1. 程式人生 > >深入理解java虛擬機器(十三) Java 即時編譯器JIT機制以及編譯優化

深入理解java虛擬機器(十三) Java 即時編譯器JIT機制以及編譯優化

在部分的商用虛擬機器中,Java 程式最初是通過直譯器( Interpreter )進行解釋執行的,當虛擬機發現某個方法或程式碼塊的執行特別頻繁的時候,就會把這些程式碼認定為“熱點程式碼”。為了提高熱點程式碼的執行效率,在執行時,即時編譯器(Just In Time Compiler )會把這些程式碼編譯成與本地平臺相關的機器碼,並進行各種層次的優化。

1、HotSpot 內的即時編譯器

直譯器和編譯器各有各的優點:

直譯器優點:當程式需要迅速啟動的時候,直譯器可以首先發揮作用,省去了編譯的時間,立即執行。解釋執行佔用更小的記憶體空間。同時,當編譯器進行的激進優化失敗的時候,還可以進行逆優化來恢復到解釋執行的狀態。

編譯器優點:在程式執行時,隨著時間的推移,編譯器逐漸發揮作用,把越來越多的程式碼編譯成原生代碼之後,可以獲得更高的執行效率。

因此,整個虛擬機器執行架構中,直譯器與編譯器經常配合工作,如下圖所示。


HotSpot中內建了兩個即時編譯器,分別稱為 Client CompilerServer Compiler ,或者簡稱為 C1 編譯器和 C2 編譯器。目前的 HotSpot 編譯器預設的是直譯器和其中一個即時編譯器配合的方式工作,具體是哪一個編譯器,取決於虛擬機器執行的模式,HotSpot 虛擬機器會根據自身版本與計算機的硬體效能自動選擇執行模式,使用者也可以使用 -client 和 -server 引數強制指定虛擬機器執行在 Client 模式或者 Server 模式。這種配合使用的方式稱為“混合模式”(Mixed Mode)

,使用者可以使用引數 -Xint 強制虛擬機器運行於 “解釋模式”(Interpreted Mode),這時候編譯器完全不介入工作。另外,使用 -Xcomp 強制虛擬機器運行於 “編譯模式”(Compiled Mode),這時候將優先採用編譯方式執行,但是直譯器仍然要在編譯無法進行的情況下接入執行過程。通過虛擬機器 -version 命令可以檢視當前預設的執行模式。


2、被編譯物件和觸發條件

在執行過程中會被即時編譯的“熱點程式碼”有兩類,即:

  • 被多次呼叫的方法
  • 被多次執行的迴圈體
對於第一種,編譯器會將整個方法作為編譯物件,這也是標準的JIT 編譯方式。對於第二種是由迴圈體出發的,但是編譯器依然會以整個方法作為編譯物件,因為發生在方法執行過程中,稱為棧上替換

判斷一段程式碼是否是熱點程式碼,是不是需要出發即時編譯,這樣的行為稱為熱點探測(Hot Spot Detection),探測演算法有兩種,分別為。

  • 基於取樣的熱點探測(Sample Based Hot Spot Detection):虛擬機器會週期的對各個執行緒棧頂進行檢查,如果某些方法經常出現在棧頂,這個方法就是“熱點方法”。好處是實現簡單、高效,很容易獲取方法呼叫關係。缺點是很難確認方法的reduce,容易受到執行緒阻塞或其他外因擾亂。
  • 基於計數器的熱點探測(Counter Based Hot Spot Detection):為每個方法(甚至是程式碼塊)建立計數器,執行次數超過閾值就認為是“熱點方法”。優點是統計結果精確嚴謹。缺點是實現麻煩,不能直接獲取方法的呼叫關係。

HotSpot 使用的是第二種-基於技術其的熱點探測,並且有兩類計數器:方法呼叫計數器(Invocation Counter )和回邊計數器(Back Edge Counter )。

這兩個計數器都有一個確定的閾值,超過後便會觸發 JIT 編譯。

首先是方法呼叫計數器。Client 模式下預設閾值是 1500 次,在 Server 模式下是 10000次,這個閾值可以通過 -XX:CompileThreadhold 來人為設定。如果不做任何設定,方法呼叫計數器統計的並不是方法被呼叫的絕對次數,而是一個相對的執行頻率,即一段時間之內的方法被呼叫的次數。當超過一定的時間限度,如果方法的呼叫次數仍然不足以讓它提交給即時編譯器編譯,那麼這個方法的呼叫計數器就會被減少一半,這個過程稱為方法呼叫計數器熱度的衰減(Counter Decay),而這段時間就成為此方法的統計的半衰週期( Counter Half Life Time)。進行熱度衰減的動作是在虛擬機器進行垃圾收集時順便進行的,可以使用虛擬機器引數 -XX:CounterHalfLifeTime 引數設定半衰週期的時間,單位是秒。整個 JIT 編譯的互動過程如下圖。


第二個回邊計數器,作用是統計一個方法中迴圈體程式碼執行的次數,在位元組碼中遇到控制流向後跳轉的指令稱為“回邊”( Back Edge )。顯然,建立回邊計數器統計的目的就是為了觸發 OSR 編譯。關於這個計數器的閾值, HotSpot 提供了 -XX:BackEdgeThreshold 供使用者設定,但是當前的虛擬機器實際上使用了 -XX:OnStackReplacePercentage 來簡介調整閾值,計算公式如下:

在 Client 模式下, 公式為 方法呼叫計數器閾值(CompileThreshold)X OSR 比率(OnStackReplacePercentage)/ 100 。其中 OSR 比率預設為 933,那麼,回邊計數器的閾值為 13995。

在 Server 模式下,公式為 方法呼叫計數器閾值(Compile Threashold)X (OSR (OnStackReplacePercentage)- 直譯器監控比率 (InterpreterProfilePercent))/100

其中 onStackReplacePercentage 預設值為 140,InterpreterProfilePercentage 預設值為 33,如果都取預設值,那麼 Server 模式虛擬機器回邊計數器閾值為 10700 。

執行過程,如下圖。


3、編譯過程

預設情況下,無論是方法呼叫產生的即時編譯請求,還是 OSR 請求,虛擬機器在程式碼編譯器還未完成之前,都仍然將按照解釋方式繼續執行,而編譯動作則在後臺的編譯執行緒中進行,使用者可以通過引數 -XX:-BackgroundCompilation 來禁止後臺編譯,這樣,一旦達到 JIT 的編譯條件,執行執行緒向虛擬機器提交便已請求之後便會一直等待,直到編譯過程完成後再開始執行編譯器輸出的原生代碼。

對於 Client 模式而言

它是一個簡單快速的三段式編譯器,主要關注點在於區域性的優化,放棄了許多耗時較長的全域性優化手段。

  1. 第一階段,一個平臺獨立的前端將位元組碼構造成一種高階中間程式碼表示(High-Level Intermediate Representaion , HIR)。在此之前,編譯器會在位元組碼上完成一部分基礎優化,如 方法內聯,常量傳播等優化。
  2. 第二階段,一個平臺相關的後端從 HIR 中產生低階中間程式碼表示(Low-Level Intermediate Representation ,LIR),而在此之前會在 HIR 上完成另外一些優化,如空值檢查消除,範圍檢查消除等,讓HIR 更為高效。
  3. 第三階段,在平臺相關的後端使用線性掃描演算法(Linear Scan Register Allocation)在 LIR 上分配暫存器,做窺孔(Peephole)優化,然後產生機器碼。 

Client Compiler 的大致執行過程如下圖所示:


對於 Server Compiler 模式而言

它是專門面向服務端的典型應用,併為服務端的效能配置特別調整過的編譯器,也是一個充分優化過的高階編譯器,幾乎能達到 GNU C++ 編譯器使用-O2 引數時的優化強度,它會執行所有的經典的優化動作,如 無用程式碼消除(Dead Code Elimination)、迴圈展開(Loop Unrolling)、迴圈表示式外提(Loop Expression Hoisting)、消除公共子表示式(Common Subexpression Elimination)、常量傳播(Constant Propagation)、基本塊衝排序(Basic Block Reordering)等,還會實施一些與 Java 語言特性密切相關的優化技術,如範圍檢查消除(Range Check Elimination)、空值檢查消除(Null Check Elimination ,不過並非所有的空值檢查消除都是依賴編譯器優化的,有一些是在程式碼執行過程中自動優化 了)等。另外,還可能根據直譯器或Client Compiler 提供的效能監控資訊,進行一些不穩定的激進優化,如 守護內聯(Guarded Inlining)、分支頻率預測(Branch Frequency Prediction)等。

Server Compiler 編譯器可以充分利用某些處理器架構,如(RISC)上的大暫存器集合。從即時編譯的角度來看, Server Compiler 無疑是比較緩慢的,但它的便以速度仍遠遠超過傳統的靜態優化編譯器,而且它相對於 Client Compiler編譯輸出的程式碼質量有所提高,可以減少原生代碼的執行時間,從而抵消了額外的編譯時間開銷,所以也有很多非服務端的應用選擇使用 Server 模式的虛擬機器執行。

相關推薦

深入理解java虛擬機器十三 Java 即時編譯器JIT機制以及編譯優化

在部分的商用虛擬機器中,Java 程式最初是通過直譯器( Interpreter )進行解釋執行的,當虛擬機發現某個方法或程式碼塊的執行特別頻繁的時候,就會把這些程式碼認定為“熱點程式碼”。為了提高熱點程式碼的執行效率,在執行時,即時編譯器(Just In Time Com

深入理解java虛擬機器java虛擬機器的記憶體區域

一、 java虛擬機器記憶體區域主要有:方法區、堆、虛擬機器棧、本地方方法棧、程式計數器     按照執行緒私有和共有來分:執行緒私有的有--程式計數器,虛擬機器棧,本地方法棧。共有的有--本地方法區,堆     1、程式計數器:主要功能是控制程式

深入理解Java虛擬機器5Java記憶體模型

深入理解Java虛擬機器(5)Java記憶體模型 Java記憶體模型 主記憶體和工作記憶體 volatile關鍵字 long與double型別的特殊規則 synchronized關鍵字 Java記憶體模

深入理解java虛擬機器java的記憶體區域

程式計數器:可以看作當前執行緒所執行的位元組碼的行號指示器,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條 需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來實現。每一個執行緒都有一個獨立的程式計數器,各個執行緒之間的計數器互不影響,獨立

深入理解Java虛擬機器--- Java 與 JVM

Java 特性 1.結構嚴謹,面向物件程式語言 2.跨平臺性 3.較安全的記憶體管理和訪問機制(避免了絕大部分記憶體洩漏和指標越界的問題) 4.實現熱點程式碼檢測和執行時編譯優化 5.擁有一套完整的API介面 6.擁有豐富的第三方庫 JVM 特性 基於棧

Java虛擬機器十三--常量池解析

解析常量池是解讀Java位元組碼檔案的的基礎。 jvm會將位元組碼檔案中的常量池資訊進行解析,並存儲到jvm記憶體模型中的常量區。 jvm使用ClassFileParser:parseClassFile()方法解析位元組碼檔案。 內部解析步驟: 解析魔數 解析

Java程式設計師從笨鳥到菜鳥之九十六深入java虛擬機器——java本地介面JNI詳解

        對於java程式設計師來說,java語言的好處和優點,我想不用我說了,大家自然會說出很多一套套的。但雖然我們作為java程式設計師,但我們不得不承認java語言也有一些它本身的缺點。比如在效能、和底層打交道方面都有它的缺點。所以java就提供了一些本地介面,他主要的作用就是提供一個標準的方式讓

深入JVM(Java虛擬機器)Java虛擬機器記憶體區域劃分

   本文為博主參閱自《深入理解Java虛擬機器:JVM高階特性與最佳實踐(第2版)》,書中的全部講解均以《Java虛擬機器規範(Java SE 7)》為依據    圖一中為JVM規範中對java虛擬機器記憶體區域的劃分及定義,為單執行緒時的粗略劃分 圖二,中所繪為JVM

深入java虛擬機器--Java語法糖

    語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家Peter.J.Landin發明的一個術語,指在計算機語言中新增的某種語法,這種語法對語言的功能並沒有影響,但是更方便程式設計師使用。Java中最常用的語法糖主要有泛型、變長引數、條件編譯、

Java程式設計師從笨鳥到菜鳥之九十五深入java虛擬機器——java虛擬機器的垃圾回收機制

         Java語言從出現到現在,一直佔據程式語言前列,他很大的一個原因就是由於java應用程式所執行的平臺有關。我們大家都知道java應用程式執行在java虛擬機器上。這樣就大大減少了java應用程式和底層作業系統打交道的頻率。這也就為java程式的跨平臺提供了良好的基礎。在java虛擬機器中

Java程式設計師從笨鳥到菜鳥之十三深入java虛擬機器——類載入器詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

深入理解java虛擬機器位元組碼指令簡介

Java虛擬機器指令是由(佔用一個位元組長度、代表某種特定操作含義的數字)操作碼Opcode,以及跟隨在其後的零至多個代表此操作所需引數的稱為運算元 Operands 構成的。由於Java虛擬機器是面向運算元棧而不是暫存器的架構,所以大多數指令都只有操作碼,而沒有運算元。 位元組碼指令集是一種具有鮮明特點、

深入理解 Java 虛擬機器方法呼叫

方法呼叫 方法呼叫不等同於方法執行,方法呼叫階段唯一任務就是確定被呼叫方法的版本(即呼叫哪一個方法),暫時還不涉及方法內部的具體執行過程。一切方法呼叫在 Class 檔案裡面儲存的都只是符號引用,需要在類載入期間,甚至到執行期間才能確定目標方法的直接引用。

深入理解Java虛擬機器——JVM整體結構與垃圾回收演算法介紹

JVM整體架構 •JVM(虛擬機器):指以軟體的方式模擬具有完整硬體系統功能、執行在一個完全隔離環境中的完整計算機系統 ,是物理機的軟體實現。常用的虛擬機器有VMWare,Virtual Box,Ja

深入理解Java虛擬機器——類載入器深入解析

類載入過程 •類載入:類載入器將class檔案載入到虛擬機器的記憶體  •載入:在硬碟上查詢並通過IO讀入位元組碼檔案 •連線:執行校驗、準備、解析(可選)步驟 •校驗:校驗位元組碼檔案的正確性

深入理解Java虛擬機器——JVM效能調優監控工具

Jinfo 檢視正在執行的Java應用程式的擴充套件引數 檢視jvm的引數 檢視java系統引數 Jstat jstat命令可以檢視堆記憶體各部分的使用量,以及載入類的數量。命

深入理解java虛擬機器

前言  本篇主要講述java記憶體區域的劃分。下面直接進入正題。 概述 java虛擬機器就是在真實物理機上虛擬出來的一臺計算機,java語言有一個特點就是可以跨平臺,其中java起著關鍵作用。這是因為它遮蔽與平臺相關的資訊,java原始檔經過編譯程式編譯後生成位元組碼檔

深入理解java虛擬機器

前言 上篇已經介紹到記憶體結構劃分《深入理解java虛擬機器一》,本篇將介紹jvm記憶體分配。 正文 這裡主要介紹物件記憶體的分配,而物件是在堆中分配記憶體的,堆可以分為新年代和老年代,其中新年代可以劃分為Eden區和Survivor區,而Survivor又可以進一步劃

深入理解java虛擬機器

前言  上篇已經介紹到記憶體結構劃分《深入理解java虛擬機器二》本篇主要講述JVM垃圾回收機制。下面直接進入正題。 正文 JVM垃圾回收機制收集的是死亡的物件,也是就是沒有任何引用的物件。那怎麼判斷物件是否死亡。 引數計數演算法 引數計數演算法會給每個物件新增一個

Java程式設計師從笨鳥到菜鳥之十三深入java虛擬機器——類的生命週期類的載入和連線

         類載入器,顧名思義,類載入器(class loader)用來載入 Java 類到 Java 虛擬機器中。一般來說,Java 虛擬機器使用 Java 類的方式如下:Java 源程式(.java 檔案)在經過 Java 編譯器編譯之後就被轉換成 Java 位元組程式碼(.class 檔案)。類載