Java類加載的整個過程 以及 JIT編譯

Introduction

java是跨平臺的編程語言。所以,它具有自己的編譯器,不像其他一些語言需要一來操作系統的瀏覽器。

在這邊文章中,我們將探討什麼是java編譯器,以及它是怎麼執行java程序的。java不像C語言直接可以從源文件生成一個可以執行的文件,而是將源文件轉化成字節碼文件(.class),字節碼文件是可以被轉移到其他任何安裝有jre的運行環境去運行的而不會影響系統內部的結構。

java使用獨立於操作系統的編譯器(javac)去編譯源文件得到字節碼文件,然後將字節碼交由JVM去執行。

編譯Compilation/Compile time

在這個過程中,一個源碼文件被編譯器從源代碼編譯成字節碼文件,以.class結尾。一個源代碼文件對應一個.class文件。等到編譯結束時,編譯器會對字節碼基本的語法和語義進行校驗。

執行Execution/Runtime

當編譯器校驗完程序的錯誤,程序員也修改好了源文件裏面相應的錯誤並重新編譯之後。重新編譯好的字節碼會被加載到JVM中,加載到JVM之後,主要會經過以下三個主要步驟:

1、ClassLoader 類加載器

主程序字節碼會被類加載器加載到系統的內存中去,同時,所有需要的或者被飲用的類也會被類加載器加載進內存。
類加載器。
類加載器分爲兩種類型:一種是原始加載器(primordial class loader ),一種是非原始加載器。
原始加載器就是系統自身自帶的加載器,是加載器的加載器。原因是每個類有它的加載器,但是加載器本身也是一個類,那麼它也需要一個加載器去加載它,原始加載器就是系統中用來加載類加載器的加載器。原始加載器通常是用C編寫,並且不會在java的上下文中顯現出來。
某些類,例如java。*包中定義的類,對於Java虛擬機和運行時系統的正確運行必不可少。它們通常被稱爲基類。由於歷史原因,所有此類都具有一個爲null的類加載器。這個空類加載器也許是原始類加載器存在的唯一標誌。實際上,將空類加載器簡單地視爲原始類加載器更爲容易。

簡言之,原始加載器和非原始加載器的區別就是,原始加載器是jvm自帶的,是不能被我們所修改的。那麼,非原始加載器是我們可以自定義的,類的加載過程完全是我們自己掌控的。

2、Bytecode Verifier字節碼校驗器

這是整個編譯過程中最關重要的一個步,它的作用是去檢查代碼是否會具有破壞性。

字節碼校驗器遍歷字節碼,構造類型狀態信息,並驗證所有字節碼指令的參數類型。

測試的範圍從簡單的驗證代碼片段格式正確到通過簡單的定理證明者傳遞每個代碼片段以證明其遵循規則:

  • 它沒有僞造指針,
  • 它沒有違反訪問限制,
  • 它按原樣訪問對象(例如,InputStream對象始終用作InputStream,而從不用作其他任何對象)。
    一種安全的語言加上生成的代碼的運行時驗證,可以建立一套基本的保證,保證不會違反接口。

字節碼校驗器充當一種看門人的角色:它確保傳遞給Java編譯器的代碼處於適合的狀態,可以執行並且可以運行,而不必擔心破壞Java解釋器。導入的代碼在通過驗證者的測試之前,不允許以任何方式執行。一旦校驗完成後,就會知道許多重要的屬性:

  • 沒有操作數棧上溢或下溢
  • 已知所有字節碼指令的參數類型始終正確
  • 已知對象字段訪問是合法的-私有,公共或受保護的

簡言之,就是會去校驗下面幾個方面:

  • 一個參數變量是否被合理地初始化
  • 使用的方法會返回要求返回的適當的值
  • 運行時棧未溢出。
  • 不會違反訪問權限修飾詞Access specifier(modifier)(如:default,private,protected,public)
    在這裏插入圖片描述
    如果字節碼校驗器發現有任何一步出現錯誤,就會中止類加載的過程並且返回一個error。
    oracle 官方關於字節碼校驗器的說明

3、Just-in-Time Compiler JIT編譯器

這是編譯過程的最後一步,JIT編譯器將字節碼轉化成機器語言,也就是0和1,然後將機器語言指令傳遞給本機,來幫助提高Java程序的性能。

JIT編譯器默認情況下處於啓用狀態,並在調用Java方法時被激活。 JIT編譯器將該方法的字節碼編譯爲本地機器代碼,“及時”編譯以運行。編譯方法後,JVM會直接調用該方法的已編譯代碼,而不是對其進行解釋。從理論上講,如果編譯不需要處理器時間和內存使用量,則編譯每種方法都可以使Java程序的速度接近本機應用程序的速度。

而JIT編譯確實需要處理器時間和內存使用率。 JVM首次啓動時,將調用數千種方法。即使程序最終達到了非常好的峯值性能,編譯所有這些方法也會嚴重影響啓動時間。

編譯字節碼成機器碼的過程是相當耗時的,那麼爲何還要對字節碼進行編譯呢?
原因在於,當一段代碼被頻繁調用的時候,比如一個循環中的循環體,這段代碼將會被執行很多次,故JIT編譯器編譯好的機器碼最終交交還給物理機執行,而不是由JVM執行。

JIT編譯器原理的詳細解釋
在這裏插入圖片描述
以下是Oracle官方的流程圖,更爲準確一些。在這裏插入圖片描述


問題補充:JIT是不是屬於JVM的一部分?

下圖是整個JDK、JRE和JVM的關係,我們可以看到在JVM(java virtual machine)處,它是包含Java HotSpot Client Compiler和Java HotSpot server Compiler的。

在這裏插入圖片描述

Client Compiler 與 Server Compiler

我們可以用修改參數的方式來指定採用Client模式和Server模式,默認情況下是mixed模式。

java -Xint 解析 java -Xcomp 編譯

Client Compiler和Server Compiler會實現分層編譯(Since JDK1.7)。
第0層 程序解析執行,解析器不開啓性能監控,可觸發第一層編譯;
第1層 編譯成本地相關代碼,進行簡單優化;
第2層 除編譯成本地相關代碼外,還進行成編譯耗時較長的優化。

Client Compiler獲得更高的編譯速度
Server Compiler獲得更好的編譯質量,無須承擔性能監控任務

這邊的HotSpot Compiler就是我們所說的JIT Compiler。

何解?
首先我們要分清三個重要概念

三個概念:

1. interpreter(解釋器/翻譯器)

  • interpreter(解釋器/翻譯器):解釋器會一行一行地解釋並執行字節碼。它的缺點是當一個方法或代碼片段(這邊主要是循環體)被多次調用時,每次都需要去執行該過程。

2. Just-In-Time Compiler(JIT編譯器)

  • Just-In-Time Compiler(JIT編譯器) :它被用來提高解釋器的執行效率。JIT編譯器會編譯整個字節碼並將它轉化成native code也就是機器碼,所以當解釋器碰到重複調用這個方法或代碼片段時,JIT compiler就會直接提供之前編譯好的機器碼,就不需要再次對他進行解釋,效率就得到了提升。

3. HotSpot code(熱點代碼)

  • HotSpot code(熱點代碼):在解釋器解釋執行一段代碼的時候,並不知道該代碼會被多次調用,所以剛開始並不會主動去編譯這段字節碼(解釋執行比編譯執行要快很多)。如果一段代碼被調用超過一定頻率,也就是被頻繁調用的時候,這段代碼就會被JIT編譯器識別爲“熱點代碼”。由於這段代碼被多次解釋執行,JIT編譯器也會在每次編譯的時候作出相應的優化。

熱點代碼包括兩類:

  • 被頻繁調用的方法
  • 頻繁執行的循環體

觸發JIT閾值的檢測:

  • 方法調用計數器(Invocation Counter):檢測方法調用的次數
  • 回邊計數器(Back Edge Counter):記錄代碼塊循環的次數
    出發了JIT編譯並完成編譯之後,這段代碼的入口就會被系統自動改爲新的編譯入口,就會直接調用編譯好的版本。

結合以上的基本概念,我們來談談JVM中代碼執行的幾種模式。

JVM中代碼執行的三種模式

1. 解釋器模式

一條一條地讀取,解釋並且執行字節碼指令。因爲它一條一條地解釋和執行指令,所以它可以很快地解釋字節碼,但是執行起來會比較慢,沒有JIT的配合下效率不高。

解釋器模式優缺點

  • 程序啓動時首先發揮作用,解釋執行Class字節碼;
  • 省去編譯時間,加快啓動速度;
  • 但執行效率較低;

2. JIT編譯器模式

即時編譯器把整段字節碼不加篩選的編譯成機器碼不論其執行頻率是否有編譯價值,在程序響應時間的限制下,沒有達到最大的優化。

JIT編譯器模式優缺點

  • 程序解釋運行後,JIT編譯器逐漸發揮作用;
  • 編譯成本地代碼,提高執行效率;
  • 但佔用程序運行時間、內存等資源;

3. 混合模式

在解釋執行的模式下引入編譯執行,剛好可以彌補相互的缺點,達到更優的效果。

參考鏈接🔗:
https://www.jianshu.com/p/25d17b7b8ff0
https://blog.csdn.net/sinat_37138973/article/details/78438806

https://blog.csdn.net/sunxianghuang/article/details/52094859
https://blog.csdn.net/HarderXin/article/details/103924865

https://blog.csdn.net/HarderXin/article/details/103924865
https://www.icode9.com/content-1-191324.html
http://blog.sina.com.cn/s/blog_1645e034e0102yg20.html

拓展鏈接🔗:java內存
https://dzone.com/articles/evolution-of-the-java-memory-architecture-java-17
https://www.cnblogs.com/rafx/p/4887145.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章