JVM系列七(JIT 即時編譯器).

来源:https://www.cnblogs.com/jmcui/archive/2020/01/17/12155840.html
-Advertisement-
Play Games

一、概述 即時編譯器(Just In Time Compiler),也稱為 JIT 編譯器,它的主要工作是把熱點代碼編譯成與本地平臺相關的機器碼,併進行各種層次的優化,從而提高代碼執行的效率。 那麼什麼是熱點代碼呢?我們知道虛擬機通過解釋器(Interpreter)來執行位元組碼文件,當虛擬機發現某個 ...


一、概述

即時編譯器(Just In Time Compiler),也稱為 JIT 編譯器,它的主要工作是把熱點代碼編譯成與本地平臺相關的機器碼,併進行各種層次的優化,從而提高代碼執行的效率。

那麼什麼是熱點代碼呢?我們知道虛擬機通過解釋器(Interpreter)來執行位元組碼文件,當虛擬機發現某個方法或代碼塊的運行特別頻繁時,就會把這些代碼認定為“熱點代碼”(Hot Spot Code)。

即時編譯器編譯性能的好壞、代碼優化程度的高低是衡量一款商用虛擬機優秀與否的關鍵指標之一,它也是虛擬機最核心且最能體現技術水平的部分。

然而,程式員在開發過程中,壓根不會感知到即時編譯器的存在,也參與不了即時編譯器的過程,所以我們對即時編譯器的學習更多的是瞭解,明白怎麼寫代碼才能更好的被即時編譯器優化。

二、工作流程

HotSpot 虛擬機包含解釋器和編譯器。它們是怎麼搭配工作的呢?當程式啟動的時候,解釋器首先發揮作用,它能直接運行位元組碼文件;隨著時間的推移,越來越多的熱點代碼被編譯器編譯成機器碼,從而獲取更高的執行效率。同時,解釋器還可以作為編譯器激進優化時的一個“逃生門”,當編譯器的激進優化手段不成立時,如載入了新類後類型繼承結構出現變化等,可以通過逆優化(Deoptimization)退回到解釋狀態繼續由解釋器執行。

編譯器又分為兩種,C1 編譯器(Client Compiler)和 C2 編譯器(Server Compiler),HotSpot 虛擬機會選擇哪個編譯器是由虛擬機運行於 Client 模式還是 Server 模式決定的。

預設情況下,虛擬機採用解釋器和一種編譯器搭配的方式工作,但是在分層編譯策略下,C1 編譯器和 C2 編譯器將會同時工作,分層編譯根據編譯器編譯、優化的規模和耗時,劃分出不同的編譯層次:

  • 第0層:程式解釋執行,解釋器不開啟性能監控功能,觸發 C1 編譯。
  • 第1層:C1 編譯,將位元組碼編譯成本地代碼,進行簡單、可靠的優化,如有必要解釋器將開始性能監控。
  • 第2層:C2 編譯,將位元組碼編譯成本地代碼,啟用一些編譯耗時較長的優化,甚至會根據性能監控信息進行一些不可靠的激進優化。

tips:

  1. 使用 “-client” 強制虛擬機運行於 Client 模式。
  2. 使用 “-server” 強制虛擬機運行於 Server 模式。
  3. 使用 “-Xint” 強制虛擬機只使用解釋器執行程式,編譯器不工作。
  4. 使用 “-Xcomp” 強制虛擬機只使用編譯器執行程式,解釋器作為編譯器的“逃生門”。
  5. 使用 “-XX:+TieredCompilation” 開啟分層編譯。虛擬機 Server 模式下預設開啟。

三、熱點代碼探測

熱點代碼分為兩種:被多次調用的方法、被多次執行的迴圈體。多次是一個很泛的概念,那麼到底什麼時候才能把熱點代碼編譯成機器碼呢?HotSpot 虛擬機採用的是計數器的方式,它為每個方法(甚至是代碼塊)建立計數器,統計執行次數,如果執行次數達到一定的閾值,就把這部分代碼編譯成機器碼。

探測“被多次調用的方法”的計數器稱為方法調用計數器(Invocation Counter),它統計的是一個方法調用的相對次數,即同一段時間內方法被調用的次數,當超過一定的時間限度,如果該方法的計數仍然不足以讓它提交給編譯器編譯,那麼該方法的計數就會被減少一半,這個過程稱為方法調用計數器熱度的衰減(Counter Decay),這段時間就被稱為此方法統計的半衰周期(Counter Half Life Time)。方法調用計數器的相關 JVM 參數如下:

  1. -XX:CompileThreshold 設置方法調用計數器的閾值,Client 模式下預設是 1500 次, Server 模式下預設是 10000 次
  2. -XX:UseCounterDecay 設置 true/false 來開啟/關閉熱度衰減,預設開啟
  3. -XX:CounterHalfLifeTime 設置半衰期的周期,單位是秒(debug 虛擬機支持)

探測“被多次執行的迴圈體”的計數器稱為回邊計數器(Back Edge Counter),它統計的是該方法迴圈執行的絕對次數,沒有計數熱度衰減的過程。回邊計數器的相關 JVM 參數如下:

  1. -XX:OnStackReplacePercentage OSR比率,Client 模式下預設是 933,Server 模式下預設是 140;
  2. -XX:InterpreterProfilePercentage 解釋器監控比率,預設值是 33
  3. Client 模式的回邊計數器閾值 = CompileThreshold * OnStackReplacePercentage/100,預設是 13995 次
  4. Server 模式的回邊計數器閾值 = CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)/100,預設是 10700 次

四、優化技術

HotSpot 的優化技術非常全面,實現起來也比較複雜,但是對於理解它們來說卻顯得沒那麼困難,我們將列舉幾項最有代表性的優化技術。

1. 方法內聯

方法內聯的重要性要優於其他優化措施,它的主要目的有兩個,一是去除方法調用的成本,二是為其他優化建立良好的基礎。

方法內聯的行為很簡單,就是把目標方法的代碼“複製”到發起調用的方法之中,避免發生真實的方法調用而已。

2. 公共子表達式消除

如果一個表達式 E 已經計算過了,並且從先前的計算到現在 E 中所有變數的值都沒有發生變化,那麼 E 的這次出現就成為了公共子表達式。對於這種表達式,沒有必要花時間再對它進行計算,只需要直接用前面計算過的表達式結果代替 E 就可以了。我們來舉個例子來模擬下它的優化過程:

    public static void main(String[] args) {
        int a = 1;
        int b = 1;
        int c = 1;
        int d = (c * b) * 12 + a + (a + b * c);
        // 1. 提取公共子表達式
        int E = c * b;
        d = E * 12 + a + (a + E);
        // 2. 代數化簡
        d = E * 13 + a * 2;
    }

3. 數組邊界檢查消除

當我們嘗試對數組越界訪問的時候,Java 會向我們拋一個 java.lang.ArrayIndexOutOfBoundsException,這對軟體開發者來說是一件很好的事情,即使沒有專門編寫防禦代碼,也可以避免大部分的溢出攻擊,但是對虛擬機來說,意味著每一次的數組訪問都帶有一次隱含的條件判定操作,即數組邊界檢查,那麼有沒有辦法消除這種檢查呢?

虛擬機一般是在即時編譯期間通過數據流分析來確定是否可以消除這種檢查,比如 foo[3] 的訪問,只有在編譯的時候確定 3 不會超過 foo.length - 1 的值,就可以判斷該次數組訪問沒有越界,就可以把數組邊界檢查消除。

4. 逃逸分析

逃逸分析的基本行為就是分析對象動態作用域:當一個對象在方法被定義後,它可能被外部方法所引用,例如作為調用參數傳遞到其他方法中,稱為方法逃逸;甚至還有可能被外部線程訪問到,譬如賦值給類變數或可以在其他線程中訪問的實例變數,稱為線程逃逸。

如果能證明一個對象不會逃逸到方法或者線程之外,則可以為這個變數進行一些高效的優化:

1) 棧上分配

如果確定一個對象不會逃逸出方法之外,假如能使用棧上分配這個對象,那大量的對象就會隨著方法的結束而自動銷毀了,垃圾收集系統的壓力將會小很多。然而遺憾的是,目前的 HotSpot 虛擬機還沒有實現這項優化。

2)同步消除

如果確定一個對象不會被其他線程訪問到,那麼這個變數就不存線上程間的爭搶,對這個變數實施的同步措施也可以消除掉。

3)標量替換

標量:無法被進一步分解的數據,比如原始數據類型(int、long以及 reference 類型等)
聚合量:可以被持續分解的數據,典型的就是 Java 中對象,它們還可以被分解成成員變數等。

標量替換指的是如果把一個 Java 對象拆散分解,根據程式訪問的情況,將其使用到的成員變數恢復到原始類型來訪問。

如果能確定一個對象不會被外部訪問,並且這個對象可以被拆散的話,那程式真正執行的時候就可能不創建這個對象,而改為直接創建它的若幹個被這個方法使用到的成員變數來代替。

tips:

  1. -XX:+DoEscapeAnalysis 手動開啟/關閉逃逸分析,預設開啟,C2 編譯器有效
  2. -XX:+PrintEscapeAnalysis 查看逃逸分析的結果(debug 虛擬機支持)
  3. -XX:+EliminateAllocations 手動開啟/關閉標量替換,預設開啟
  4. -XX:+PrintEliminateAllocations 查看標量替換情況(debug 虛擬機支持)
  5. -XX:+EliminateLocks 手動開啟/關閉同步消除,預設開啟

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 你可以訪問 "碼雲 樂優商城" 來獲取關於樂優商城的工程代碼。 你可以訪問 "百度雲 樂優優商城" 密碼:ppzy 來獲取關於樂優商城的資料。 一、創建父工程 Maven Project 用來管理依賴 GroupId:項目中唯一標識符,對應的是java中的包結構,在這裡表示項目中的結構 Artifa ...
  • 之前我們用SSM或者SSH進行JAVA WEB開發的時候,IDEA 需要配置Tomcat然後把項目放到tomcat運行,tomcat啟動的時候會自動打開瀏覽器去訪問項目,但是SpringBoot是內嵌tomcat的,項目啟動成功後無法自主訪問,需要我們手動打開瀏覽器輸入url訪問,我覺得這樣很不習慣... ...
  • 前言 Golang 目前的主要應用領域還是後臺微服務,雖然在業務領域也有所應用但仍然是比較小衆的選擇。大多數的服務運行環境都是linux,而在windows中golang應用更少,而作者因爲特殊情況,不得已要在widows環境中用golang去寫本地代理服務。在我的使用場景中實時性要求非常高(視頻通 ...
  • import java.io.*; public class test13_6 { public static void main(String []args) throws Exception { FileOutputStream output=null; FileInputStream inpu ...
  • 原文:https://www.jianshu.com/p/e88d3f8151db JWT官網: https://jwt.io/ JWT(Java版)的github地址:https://github.com/jwtk/jjwt 什麼是JWT Json web token (JWT), 是為了在網路應 ...
  • 一、Unittest 單元測試框架,可用於自動化測試用力組織,執行,輸出結果 二、Unittest構成 1. Test Case 2. Test Suite 3. Test Fixture 4. Test Runner (圖片來源於網路) Test Case 一個測試用例是一個獨立的測試單元。它檢查 ...
  • 你可以訪問 "碼雲 樂優商城" 來獲取關於樂優商城的工程代碼。 你可以訪問 "百度雲 樂優優商城" 密碼:ppzy 來獲取關於樂優商城的資料。 一、介紹 樂優商城是一個全品類的電商購物網站(B2C),作為階段性學習工程。 二、軟體架構 三、前端技術選型 1. 基礎的HTML、CSS、JavaScri ...
  • 效果圖 首先是資料庫 /* Navicat MySQL Data Transfer Source Server : xm Source Server Version : 50553 Source Host : localhost:3306 Source Database : test Target ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...