日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

java虚拟机源码怎么看_java虚拟机JVM第4讲:从源代码到机器码,发生了什么?

發(fā)布時(shí)間:2023/12/2 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java虚拟机源码怎么看_java虚拟机JVM第4讲:从源代码到机器码,发生了什么? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在上篇文章我們聊到,無(wú)論什么語(yǔ)言寫(xiě)的代碼,其到最后都是通過(guò)機(jī)器碼運(yùn)行的,無(wú)一例外。那么對(duì)于 Java 語(yǔ)言來(lái)說(shuō),其從源代碼到機(jī)器碼,這中間到底發(fā)生了什么呢?這就是今天我們要聊的。

如下圖所示,編譯器可以分為:前端編譯器、JIT 編譯器和AOT編譯器。下面我們逐個(gè)講解。

前端編譯器:源代碼到字節(jié)碼

之前我們說(shuō)到:對(duì)于 Java 虛擬機(jī)來(lái)說(shuō),其實(shí)際輸入的是字節(jié)碼文件,而不是 Java 文件。那么對(duì)于 Java 語(yǔ)言而言,其實(shí)怎么將 Java 代碼轉(zhuǎn)化成字節(jié)碼文件的呢?我們知道在 JDK 的安裝目錄里有一個(gè) javac 工具,就是它將 Java 代碼翻譯成字節(jié)碼,這個(gè)工具我們叫做編譯器。相對(duì)于后面要講的其他編譯器,其因?yàn)樘幱诰幾g的前期,因此又被成為前端編譯器。

通過(guò) javac 編譯器,我們可以很方便地將 java 源文件翻譯成字節(jié)碼文件。就拿我們最熟悉的 Hello World 作為例子:

public class Demo{

public static void main(String args[]){

System.out.println("Hello World!");

}

}

我們使用 javac 命令編譯上面這個(gè)類(lèi),便會(huì)生成一個(gè) Demo.class 文件:

> javac Demo.java

> ls

Demo.java Demo.class

我們使用純文本編輯器打開(kāi) Demo.class 文件,我們會(huì)發(fā)現(xiàn)是一連串的 16 進(jìn)制二進(jìn)制流。

我們運(yùn)行 javac 命令的過(guò)程,其實(shí)就是 javac 編譯器解析 Java 源代碼,并生成字節(jié)碼文件的過(guò)程。說(shuō)白了,其實(shí)就是使用 javac 編譯器把 Java 語(yǔ)言規(guī)范轉(zhuǎn)化為字節(jié)碼語(yǔ)言規(guī)范。javac 編譯器的處理過(guò)程可以分為下面四個(gè)階段:

第一個(gè)階段:詞法、語(yǔ)法分析。在這個(gè)階段,JVM 會(huì)對(duì)源代碼的字符進(jìn)行一次掃描,最終生成一個(gè)抽象的語(yǔ)法樹(shù)。簡(jiǎn)單地說(shuō),在這個(gè)階段 JVM 會(huì)搞懂我們的代碼到底想要干嘛。就像我們分析一個(gè)句子一樣,我們會(huì)對(duì)句子劃分主謂賓,弄清楚這個(gè)句子要表達(dá)的意思一樣。

第二個(gè)階段:填充符號(hào)表。我們知道類(lèi)之間是會(huì)互相引用的,但在編譯階段,我們無(wú)法確定其具體的地址,所以我們會(huì)使用一個(gè)符號(hào)來(lái)替代。在這個(gè)階段做的就是類(lèi)似的事情,即對(duì)抽象的類(lèi)或接口進(jìn)行符號(hào)填充。等到類(lèi)加載階段,JVM 會(huì)將符號(hào)替換成具體的內(nèi)存地址。

第三個(gè)階段:注解處理。我們知道 Java 是支持注解的,因此在這個(gè)階段會(huì)對(duì)注解進(jìn)行分析,根據(jù)注解的作用將其還原成具體的指令集。

第四個(gè)階段:分析與字節(jié)碼生成。到了這個(gè)階段,JVM 便會(huì)根據(jù)上面幾個(gè)階段分析出來(lái)的結(jié)果,進(jìn)行字節(jié)碼的生成,最終輸出為 class 文件。

我們一般稱(chēng) javac 編譯器為前端編譯器,因?yàn)槠浒l(fā)生在整個(gè)編譯的前期。常見(jiàn)的前端編譯器有 Sun 的 javac,Eclipse JDT 的增量式編譯器(ECJ)。

JIT 編譯器:從字節(jié)碼到機(jī)器碼

當(dāng)源代碼轉(zhuǎn)化為字節(jié)碼之后,其實(shí)要運(yùn)行程序,有兩種選擇。一種是使用 Java 解釋器解釋執(zhí)行字節(jié)碼,另一種則是使用 JIT 編譯器將字節(jié)碼轉(zhuǎn)化為本地機(jī)器代碼。

這兩種方式的區(qū)別在于,前者啟動(dòng)速度快但運(yùn)行速度慢,而后者啟動(dòng)速度慢但運(yùn)行速度快。至于為什么會(huì)這樣,其原因很簡(jiǎn)單。因?yàn)榻忉屍鞑恍枰?JIT 編譯器一樣,將所有字節(jié)碼都轉(zhuǎn)化為機(jī)器碼,自然就少去了優(yōu)化的時(shí)間。而當(dāng) JIT 編譯器完成第一次編譯后,其會(huì)將字節(jié)碼對(duì)應(yīng)的機(jī)器碼保存下來(lái),下次可以直接使用。而我們知道,機(jī)器碼的運(yùn)行效率肯定是高于 Java 解釋器的。所以在實(shí)際情況中,為了運(yùn)行速度以及效率,我們通常采用兩者相結(jié)合的方式進(jìn)行 Java 代碼的編譯執(zhí)行。

在 HotSpot 虛擬機(jī)內(nèi)置了兩個(gè)即時(shí)編譯器,分別稱(chēng)為 Client Compiler 和Server Compiler。這兩種不同的編譯器衍生出兩種不同的編譯模式,我們分別稱(chēng)之為:C1 編譯模式,C2 編譯模式。

注意:現(xiàn)在許多人習(xí)慣上將 Client Compiler 稱(chēng)為 C1 編譯器,將 Server Compiler 稱(chēng)為 C2 編譯器,但在 Oracle 官方文檔中將其描述為 compiler mode(編譯模式)。所以說(shuō) C1 編譯器、C2 編譯器只是我們自己的習(xí)慣性稱(chēng)呼,并不是官方的說(shuō)法。這點(diǎn)需要特別注意。

那么 C1 編譯模式和 C2 編譯模式有什么區(qū)別呢?

C1 編譯模式會(huì)將字節(jié)碼編譯為本地代碼,進(jìn)行簡(jiǎn)單、可靠的優(yōu)化,如有必要將加入性能監(jiān)控的邏輯。而 C2 編譯模式,也是將字節(jié)碼編譯為本地代碼,但是會(huì)啟用一些編譯耗時(shí)較長(zhǎng)的優(yōu)化,甚至?xí)鶕?jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化。

簡(jiǎn)單地說(shuō) C1 編譯模式做的優(yōu)化相對(duì)比較保守,其編譯速度相比 C2 較快。而 C2 編譯模式會(huì)做一些激進(jìn)的優(yōu)化,并且會(huì)根據(jù)性能監(jiān)控做針對(duì)性?xún)?yōu)化,所以其編譯質(zhì)量相對(duì)較好,但是耗時(shí)更長(zhǎng)。

那么到底應(yīng)該選擇 C1 編譯模式還是 C2 編譯模式呢?

實(shí)際上對(duì)于 HotSpot 虛擬機(jī)來(lái)說(shuō),其一共有三種運(yùn)行模式可選,分別是:

混合模式(Mixed Mode) 。即 C1 和 C2 兩種模式混合起來(lái)使用,這是默認(rèn)的運(yùn)行模式。如果你想單獨(dú)使用 C1 模式或 C2 模式,使用 -client 或 -server 打開(kāi)即可。

解釋模式(Interpreted Mode)。即所有代碼都解釋執(zhí)行,使用 -Xint 參數(shù)可以打開(kāi)這個(gè)模式。

編譯模式(Compiled Mode)。 此模式優(yōu)先采用編譯,但是無(wú)法編譯時(shí)也會(huì)解釋執(zhí)行,使用 -Xcomp 打開(kāi)這種模式。

在命令行中輸入 java -version 可以看到,我機(jī)器上的虛擬機(jī)使用 Mixed Mode 運(yùn)行模式。

寫(xiě)到這里,我們了解了從 Java 源代碼到字節(jié)碼,再?gòu)淖止?jié)碼到機(jī)器碼的全過(guò)程。本來(lái)到這里就應(yīng)該結(jié)束了,但在我們 Java 中還有一個(gè) AOT 編譯器,它能直接將源代碼轉(zhuǎn)化為機(jī)器碼。

AOT 編譯器:源代碼到機(jī)器碼

AOT 編譯器的基本思想是:在程序執(zhí)行前生成 Java 方法的本地代碼,以便在程序運(yùn)行時(shí)直接使用本地代碼。

但是 Java 語(yǔ)言本身的動(dòng)態(tài)特性帶來(lái)了額外的復(fù)雜性,影響了 Java 程序靜態(tài)編譯代碼的質(zhì)量。例如 Java 語(yǔ)言中的動(dòng)態(tài)類(lèi)加載,因?yàn)?AOT 是在程序運(yùn)行前編譯的,所以無(wú)法獲知這一信息,所以會(huì)導(dǎo)致一些問(wèn)題的產(chǎn)生。類(lèi)似的問(wèn)題還有很多,這里就不一一舉例了。

總的來(lái)說(shuō),AOT 編譯器從編譯質(zhì)量上來(lái)看,肯定比不上 JIT 編譯器。其存在的目的在于避免 JIT 編譯器的運(yùn)行時(shí)性能消耗或內(nèi)存消耗,或者避免解釋程序的早期性能開(kāi)銷(xiāo)。

在運(yùn)行速度上來(lái)說(shuō),AOT 編譯器編譯出來(lái)的代碼比 JIT 編譯出來(lái)的慢,但是比解釋執(zhí)行的快。而編譯時(shí)間上,AOT 也是一個(gè)始終的速度。所以說(shuō),AOT 編譯器的存在是 JVM 犧牲質(zhì)量換取性能的一種策略。就如 JVM 其運(yùn)行模式中選擇 Mixed 混合模式一樣,使用 C1 編譯模式只進(jìn)行簡(jiǎn)單的優(yōu)化,而 C2 編譯模式則進(jìn)行較為激進(jìn)的優(yōu)化。充分利用兩種模式的優(yōu)點(diǎn),從而達(dá)到最優(yōu)的運(yùn)行效率。

總結(jié)

在 JVM 中有三個(gè)非常重要的編譯器,它們分別是:前端編譯器、JIT 編譯器、AOT 編譯器。

前端編譯器,最常見(jiàn)的就是我們的 javac 編譯器,其將 Java 源代碼編譯為 Java 字節(jié)碼文件。JIT 即時(shí)編譯器,最常見(jiàn)的是 HotSpot 虛擬機(jī)中的 Client Compiler 和 Server Compiler,其將 Java 字節(jié)碼編譯為本地機(jī)器代碼。而 AOT 編譯器則能將源代碼直接編譯為本地機(jī)器碼。這三種編譯器的編譯速度和編譯質(zhì)量如下:

編譯速度上,解釋執(zhí)行 > AOT 編譯器 > JIT 編譯器。

編譯質(zhì)量上,JIT 編譯器 > AOT 編譯器 > 解釋執(zhí)行。

而在 JVM 中,通過(guò)這幾種不同方式的配合,使得 JVM 的編譯質(zhì)量和運(yùn)行速度達(dá)到最優(yōu)的狀態(tài)。

參考資料

深入理解JVM之前端編譯器(一) | Gs Chen’s blog

深入淺出 JIT 編譯器

如何通俗易懂地介紹「即時(shí)編譯」(JIT),它的優(yōu)點(diǎn)和缺點(diǎn)是什么? - 知乎

做一個(gè) Hello World 級(jí)別的 JIT 編譯器

JIT中的C1和C2編譯器 - 簡(jiǎn)書(shū)

Understanding Java JIT Compilation with JITWatch, Part 1 Oracle 官方文檔

HotSpot VM 想研究HotSpot C2編譯器編譯過(guò)程,請(qǐng)教如何入手? - 討論 - 高級(jí)語(yǔ)言虛擬機(jī) - ITeye群組 深入研究 HotSpot C2 的編譯過(guò)程

總結(jié)

以上是生活随笔為你收集整理的java虚拟机源码怎么看_java虚拟机JVM第4讲:从源代码到机器码,发生了什么?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。