若是你是一名 Java 開發人員,你確定指定 Java 代碼有不少種不一樣的運行方式。好比說能夠在開發工具(IDEA、Eclipse等)中運行,能夠雙擊執行 jar 文件運行,也能夠在命令行中運行,甚至能夠在網頁(好比各類 OJ)中運行。固然,這些執行方式都離不開 JRE(Java 運行時環境)。java
JRE 包含運行 Java 程序的必需組件,包括 JVM(Java 虛擬機)以及 Java 核心類庫等。Java 程序員常常接觸到的 JDK(Java 開發工具包)一樣包含了 JRE,而且還附帶了一系列開發、診斷工具。程序員
本篇文章主要針對如下兩個問題和你們一塊兒探討:算法
Java 的一個很是重要的特色就是與平臺的無關性,而使用 JVM 是實現這一特色的關鍵。Java 做爲一門高級程序語言,語法複雜,抽象程度高。所以,直接在硬件上運行這種複雜的程序並不現實。因此在運行 Java 程序以前,咱們須要對其進行轉換。數組
設計一個面向 Java 語言特性的虛擬機,並經過編譯器將 Java 程序轉換成該虛擬機所能識別的指令序列(由於 Java 字節碼指令的操做碼(opcode)被固定爲一個字節,故又稱 Java 字節碼)。安全
JVM 通常是在各個現有平臺(如 Windows、Linux)上提供軟件實現,這樣可使一旦一個程序被轉換成 Java 字節碼,那麼即可以在不一樣平臺上的虛擬機實現裏運行(一次編寫,處處運行)。服務器
JVM 另一個好處是帶有託管環境(Managed Runtime),託管環境可以代替處理一些代碼中冗長並且容易出錯的部分,其中包括自動內存管理與垃圾回收(GC)。jvm
另外,託管環境還提供了諸如數組越界、動態類型、安全權限等等的動態檢測,使咱們免於書寫這些無關業務邏輯的代碼。工具
JVM 具體是怎麼運行 Java 字節碼的呢?下面咱們一塊兒來看一下:性能
從 JVM 來看,執行 Java 代碼首先須要將它編譯而成的 class 文件加載到 JVM 中。加載後的 Java 類會被存放於方法區(Method Area)中。實際運行時,JVM 會執行方法區內的代碼。開發工具
JVM 會在內存中劃分出堆和棧來存儲運行時數據,JVM 會將棧細分爲面向 Java 方法的 Java 方法棧,面向本地方法(用 C++ 寫的 native 方法)的本地方法棧,以及存放各個線程執行位置的 PC 寄存器。
在運行過程當中,每當調用進入一個 Java 方法,JVM 會在當前線程的 Java 方法棧中生成一個棧幀,用以存放局部變量以及字節碼的操做數。棧幀的大小是提早計算好的,並且 JVM 不要求棧幀在內存空間裏連續分佈。
當退出當前執行的方法時,不論是正常返回仍是異常返回,JVM 均會彈出當前線程的當前棧幀,並將之捨棄。
從硬件視角來看,Java 字節碼沒法直接執行。所以,JVM 須要將字節碼翻譯成機器碼。
在 HotSpot 裏面,上述翻譯過程有兩種形式:第一種是解釋執行(interpreter),即逐條將字節碼翻譯成機器碼並執行;第二種是即時編譯(Just-In-Time compilation,JIT),即將一個方法中包含的全部字節碼編譯成機器碼後再執行。
前者的優點在於無需等待編譯,然後者的優點在於實際運行速度更快。HotSpot 默認採用混合模式,綜合瞭解釋執行和即時編譯二者的優勢。它會先解釋執行字節碼,然後將其中反覆執行的熱點代碼,以方法爲單位進行即時編譯。
整個 Java 代碼執行過程以下:
其中,在運行過程當中會被即時編譯的熱點代碼有兩類:
針對第一類,編譯器會將整個方法做爲編譯對象,這也是標準的 JIT 編譯方式。對於第二類是由循環體出發的,可是編譯器依然會以整個方法做爲編譯對象,由於發生在方法執行過程當中,稱爲棧上替換。
HotSpot 採用了多種技術來提高啓動性能以及峯值性能,剛剛提到的即時編譯即是其中最重要的技術之一。
即時編譯創建在程序符合二八定律的假設上,也就是百分之二十的代碼佔據了百分之八十的計算資源。
對於佔據大部分的不經常使用的代碼,咱們無需耗費時間將其編譯成機器碼,而是採起解釋執行的方式運行;另外一方面,對於僅佔據小部分的熱點代碼,咱們則能夠將其編譯成機器碼,以達到理想的運行速度。
爲了知足不一樣用戶場景的須要,HotSpot 內置了多個即時編譯器:C一、C2。之因此引入多個即時編譯器,是爲了在編譯時間和生成代碼的執行效率之間進行取捨。
從 Java 7 開始,HotSpot 默認採用分層編譯的方式:熱點方法首先會被 C1 編譯,然後熱點方法中的熱點會進一步被 C2 編譯。
爲了避免干擾應用的正常運行,HotSpot 的即時編譯是放在額外的編譯線程中進行的。HotSpot 會根據 CPU 的數量設置編譯線程的數目,而且按 1:2 的比例配置給 C1 及 C2 編譯器。
在計算資源充足的狀況下,字節碼的解釋執行和即時編譯可同時進行。編譯完成後的機器碼會在下次調用該方法時啓用,以替換本來的解釋執行。
其中判斷一段代碼是否爲熱點代碼,是否是須要觸發即時編譯,這樣的行爲稱爲熱點探測(Hot Spot Detection),探測算法有兩種:
HotSpot 使用的是第二種-基於計數器的熱點探測,而且有兩類計數器:方法調用計數器(Invocation Counter)和回邊計數器(Back Edge Counter)。
這篇文章主要介紹了爲何須要 JVM 以及 JVM 是怎樣運行 Java 代碼的。
爲何須要 JVM:
JVM 將運行時內存區域劃分爲五個部分,分別爲方法區、堆、PC 寄存器、Java 方法棧和本地方法棧。Java 程序編譯而成的 class 文件,須要先加載至方法區中,方能在 JVM 中運行。
爲了提升運行效率,HotSpot 虛擬機採用的是一種混合執行的策略,會解釋執行 Java 字節碼,而後會將其中反覆執行的熱點代碼,以方法爲單位進行即時編譯,翻譯成機器碼後直接運行在底層硬件之上。
HotSpot 裝載了多個不一樣的即時編譯器,以便在編譯時間和生成代碼的執行效率之間作取捨。
判斷熱點代碼的探測算法包括基於採樣和基於計數器兩種,HotSpot 採用基於計數器的熱點探測,計數器又分爲方法調用計數器和回邊計數器。