Java程式碼到底是如何編譯成機器指令的。
在《Java程式碼的編譯與反編譯》中,有過關於Java語言的編譯和反編譯的介紹。我們可以通過javac
命令將Java程式的原始碼編譯成Java位元組碼,即我們常說的class檔案。這是我們通常意義上理解的編譯。
但是,位元組碼並不是機器語言,要想讓機器能夠執行,還需要把位元組碼翻譯成機器指令。這個過程是Java虛擬機器做的,這個過程也叫編譯。是更深層次的編譯。
在編譯原理中,把原始碼翻譯成機器指令,一般要經過以下幾個重要步驟:
根據完成任務不同,可以將編譯器的組成部分劃分為前端(Front End)與後端(Back End)。
前端編譯主要指與源語言有關但與目標機無關的部分,包括詞法分析、語法分析、語義分析與中間程式碼生成。
後端編譯主要指與目標機有關的部分,包括程式碼優化和目的碼生成等。
我們可以把將.java
檔案編譯成.class
的編譯過程稱之為前端編譯。把將.class
檔案翻譯成機器指令的編譯過程稱之為後端編譯。
Java中的前端編譯
前端編譯主要指與源語言有關但與目標機無關的部分,包括詞法分析、語法分析、語義分析與中間程式碼生成。
我們所熟知的javac
的編譯就是前端編譯。除了這種以外,我們使用的很多IDE,如eclipse,idea等,都內建了前端編譯器。主要功能就是把.java
程式碼轉換成.class
程式碼。
詞法分析
詞法分析階段是編譯過程的第一個階段。這個階段的任務是從左到右一個字元一個字元地讀入源程式,將字元序列轉換為標記(token)序列的過程。這裡的標記是一個字串,是構成原始碼的最小單位。在這個過程中,詞法分析器還會對標記進行分類。
詞法分析器通常不會關心標記之間的關係(屬於語法分析的範疇),舉例來說:詞法分析器能夠將括號識別為標記,但並不保證括號是否匹配。
語法分析
語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如“程式”,“語句”,“表示式”等等.語法分析程式判斷源程式在結構上是否正確.源程式的結構由上下文無關文法描述。
語義分析
語義分析是編譯過程的一個邏輯階段, 語義分析的任務是對結構上正確的源程式進行上下文有關性質的審查,進行型別審查。語義分析是審查源程式有無語義錯誤,為程式碼生成階段收集型別資訊。
語義分析的一個重要部分就是型別檢查。比如很多語言要求陣列下標必須為整數,如果使用浮點數作為下標,編譯器就必須報錯。再比如,很多語言允許某些型別轉換,稱為自動型別轉換。
中間程式碼生成
在源程式的語法分析和語義分析完成之後,很多編譯器生成一個明確的低階的或類機器語言的中間表示。該中間表示有兩個重要的性質: 1.易於生成; 2.能夠輕鬆地翻譯為目標機器上的語言。
在Java中,javac
執行的結果就是得到一個位元組碼,而這個位元組碼其實就是一種中間程式碼。
PS:著名的解語法糖操作,也是在javac中完成的。
Java中的後端編譯
首先,我們大家都知道,通常通過 javac
將程式原始碼編譯,轉換成 java 位元組碼,JVM 通過解釋位元組碼將其翻譯成對應的機器指令,逐條讀入,逐條解釋翻譯。很顯然,經過解釋執行,其執行速度必然會比可執行的二進位制位元組碼程式慢很多。這就是傳統的JVM的直譯器(Interpreter)的功能。為了解決這種效率問題,引入了 JIT 技術。
JAVA程式還是通過直譯器進行解釋執行,當JVM發現某個方法或程式碼塊執行特別頻繁的時候,就會認為這是“熱點程式碼”(Hot Spot Code)。然後JIT會把部分“熱點程式碼”翻譯成本地機器相關的機器碼,並進行優化,然後再把翻譯後的機器碼快取起來,以備下次使用。
HotSpot虛擬機器中內建了兩個JIT編譯器:Client Complier和Server Complier,分別用在客戶端和服務端,目前主流的HotSpot虛擬機器中預設是採用直譯器與其中一個編譯器直接配合的方式工作。
當 JVM 執行程式碼時,它並不立即開始編譯程式碼。首先,如果這段程式碼本身在將來只會被執行一次,那麼從本質上看,編譯就是在浪費精力。因為將程式碼翻譯成 java 位元組碼相對於編譯這段程式碼並執行程式碼來說,要快很多。第二個原因是最優化,當 JVM 執行某一方法或遍歷迴圈的次數越多,就會更加了解程式碼結構,那麼 JVM 在編譯程式碼的時候就做出相應的優化。
在機器上,執行java -version
命令就可以看到自己安裝的JDK中JIT是哪種模式:
上圖是我的機器上安裝的jdk1.8,可以看到,他是Server Compile,但是,需要說明的是,無論是Client Complier還是Server Complier,直譯器與編譯器的搭配使用方式都是混合模式,即上圖中的mixed mode。
熱點檢測
上面我們說過,要想觸發JIT,首先需要識別出熱點程式碼。目前主要的熱點程式碼識別方式是熱點探測(Hot Spot Detection),有以下兩種:
1、基於取樣的方式探測(Sample Based Hot Spot Detection) :週期性檢測各個執行緒的棧頂,發現某個方法經常出險在棧頂,就認為是熱點方法。好處就是簡單,缺點就是無法精確確認一個方法的熱度。容易受執行緒阻塞或別的原因干擾熱點探測。
2、基於計數器的熱點探測(Counter Based Hot Spot Detection)。採用這種方法的虛擬機器會為每個方法,甚至是程式碼塊建立計數器,統計方法的執行次數,某個方法超過閥值就認為是熱點方法,觸發JIT編譯。
在HotSpot虛擬機器中使用的是第二種——基於計數器的熱點探測方法,因此它為每個方法準備了兩個計數器:方法呼叫計數器和回邊計數器。
方法計數器。顧名思義,就是記錄一個方法被呼叫次數的計數器。
回邊計數器。是記錄方法中的for或者while的執行次數的計數器。
編譯優化
前面提到過,JIT除了具有快取的功能外,還會對程式碼做各種優化。說到這裡,不得不佩服HotSpot的開發者,他們在JIT中對於程式碼優化真的算是面面俱到了。
這裡簡答提及幾個我覺得比較重要的優化技術,並不準備直接展開,讀者感興趣的話,我後面再寫文章單獨介紹。
相關推薦
Java程式碼到底是如何編譯成機器指令的。
在《Java程式碼的編譯與反編譯》中,有過關於Java語言的編譯和反編譯的介紹。我們可以通過javac命令將Java程式的原始碼編譯成Java位元組碼,即我們常說的class檔案。這是我們通常意義上理解的編譯。 但是,位元組碼並不是機器語言,要想讓機器能夠執行,還需要把
[四] java虛擬機器JVM編譯器編譯程式碼簡介 位元組碼指令例項 程式碼到底編譯成了什麼形式
public static void main(String[] args) { boolean bNum = true; char cNum = 2; byte byteNum = 127; short sNum = 32767; int iNum = 100; long lNum =
java程式碼中得到本地ip地址。獲取客戶端請求IP(終端使用者的網際網路或內網IP地址)
private static String getLocalIP() throws Exception{ String localIP = ""; InetAddress addr = (InetAddress) InetAddress.getLocalHos
eclipse如何把java程式碼,打包成jar檔案以及轉換為exe可執行檔案
1、背景: 學習java時,教材中關於如題問題,只有一小節說明,而且要自己寫麻煩的配置檔案,最終結果卻只能轉換為jar檔案。實在是心有不爽。此篇部落格教你如何方便快捷地把java程式碼,打包成jar檔案以及轉換為exe可執行檔案 2、前言: 我們都知道
eclipse -- 手把手教你如何把java程式碼,打包成jar檔案以及轉換為exe可執行檔案
1、背景: 學習java時,教材中關於如題問題,只有一小節說明,而且要自己寫麻煩的配置檔案,最終結果卻只能轉換為jar檔案。實在是心有不爽。此篇部落格教你如何方便快捷地把java程式碼,打包成jar檔案以及轉換為exe可執行檔案 2、前言: 我們都知道Java可以將二進位制程式打包成可執
Java程式碼的編譯與反編譯那些事兒
程式語言 在介紹編譯和反編譯之前,我們先來簡單介紹下程式語言(Programming Language)。程式語言(Programming Language)分為低階語言(Low-level Language)和高階語言(High-level Language)。 機器語言(Machine Language)
【eclipse jar包】在編寫java程式碼時,為方便程式設計,常常會引用別人已經實現的方法,通常會封裝成jar包,我們在編寫時,只需引入到Eclipse中即可。
Eclipse中匯入外部jar包 在編寫java程式碼時,為方便程式設計,常常會引用別人已經實現的方法,通常會封裝成jar包,我們在編寫時,只需引入到Eclipse中即可。 工具/原料 Eclipse
Java中的String,javap&cfr.jar反編譯,javap反編譯後二進位制指令程式碼詳解,Java8常量池的位置
一個例子 public class TestString{ public static void main(String[] args){ String a = "a"; String b = a+"b";
《Java虛擬機原理圖解》4.JVM機器指令集
popu 符號 處理 組織 snippet add ide 獲取 alt 0. 前言 Java虛擬機和真實的計算機一樣,執行的都是二進制的機器碼;而我們將.java 源碼編譯成.class 文件,class文件便是Java虛擬機可以認識的
如何通過jad把class批量反編譯成java文件
完全 之前 文件夾 文章 cli 自己的 官網 定義 沒有 背景:前幾天在項目開發的時候遇到一個問題,那就是利用myeclipse編寫好的一個項目打包成jar包後上傳部署到服務器裏,之後本地的項目被自己改來改去出現了一些問題,想著把上傳到服務器裏面的war包下載下來重新利用
CCF認證201803-2 碰撞的小球 java程式碼實現。
問題描述 數軸上有一條長度為L(L為偶數)的線段,左端點在原點,右端點在座標L處。有n個不計體積的小球線上段上,開始時所有的小球都處在偶數座標上,速度方向向右,速度大小為1單位長度每秒。 當小球到達線段的端點(左端點或右端點)的時候,會立即向相反的方向移動,速度大小仍然為原來大小。 當兩個小
android_反編譯java程式碼
下載dex2jar-2.0.zip找個最新版的下了就行 http://sourceforge.net/projects/dex2jar/files/ 這個工具用於將dex檔案轉換成jar檔案(dex檔案是什麼先不用考慮) 之後解壓這個dex2jar-2.0.zip 找一個自己寫好的程式 匯
Java編譯(二) Java前端編譯:Java原始碼編譯成Class檔案的過程
Java編譯(二)Java前端編譯: Java原始碼編譯成Class檔案的過程 在上篇文章《Java三種編譯方式:前端編
所謂的網頁爬蟲用java程式碼來實現,此程式碼適合在maven專案中使用中使用,因為,程式碼中的類所對應的依賴可以讓maven下載。
//獲得httpClient物件 CloseableHttpClient httpClient = HttpClients.createDefault(); //url公司域名隨便 String url = "https://www.baidu.co
war反編譯成java專案
第一步:到伺服器下載專案並將.war改成.zip 第二步:下載jar反編譯工具jd-gui 下載地
通過自己的java程式碼打成parcel包,並生成sha檔案
以自己寫的一個java程式為例 1. 製作parcel包 1.1 我的程式結構如下: [[email protected] nameaddress-1.0]# cd nameaddress/ [[email protected] nameaddress]# ll 總用
Java 程式碼編譯和執行的整個過程
Java 位元組碼的執行是由 JVM 執行引擎來完成,流程圖如下所示: Java 程式碼編譯和執行的整個過程包含了以下三個重要的機制: Java 原始碼編譯機制 類載入機制 類執行機制 Java 原始碼編譯機制 Java 原始碼編譯由以下三個過程組成: 分析和輸入到符
深入拆解虛擬機器(一)Java程式碼是咋樣執行的
Java執行方式 序號 方式 1 開發工具中執行 2 雙擊Jar檔案執行 3 命令列中執行
Java程式碼編譯過程簡述
程式碼編譯是由Javac編譯器來完成, 這是由.java原始碼檔案轉為 .class二進位制位元組碼檔案的過程。 詳細過程: 原始碼檔案*.java -> 詞法分析器 -> tokens流 -&
【資料結構與演算法】貪心演算法解決揹包問題。java程式碼實現
揹包問題(貪心演算法) 貪心演算法思想 簡單的說,就是將大問題轉化為最優子問題,例如本題所要求的,揹包容量有限,要想使物品的總價值最高,那麼,我們必須儘可能的選擇權重高的(即單位價值更高)的物品進行裝載。 在揹包問題中,物品是可拆的,即可以分成任意部分進行裝載,而最終實現的目標是