面試官:Java從編譯到執行,發生了什麼?

Java3y發表於2021-10-18

面試官:今天從基礎先問起吧,你是怎麼理解Java是一門「跨平臺」的語言,也就是「一次編譯,到處執行的」?

候選者:很好理解啊,因為我們有JVM。

候選者:Java原始碼會被編譯為class檔案,class檔案是執行在JVM之上的。

候選者:當我們日常開發安裝JDK的時候,可以發現JDK是分「不同的作業系統」,JDK裡是包含JVM的,所以Java依賴著JVM實現了『跨平臺』

候選者:JVM是面向作業系統的,它負責把Class位元組碼解釋成系統所能識別的指令並執行,同時也負責程式執行時記憶體的管理。

面試官那要不你來聊聊從原始碼檔案(.java)到程式碼執行的過程唄?

候選者:嗯,沒問題的

候選者:簡單總結的話,我認為就4個步驟:編譯->載入->解釋->執行

候選者:編譯:將原始碼檔案編譯成JVM可以解釋的class檔案。

候選者:編譯過程會對原始碼程式做 「語法分析」「語義分析」「註解處理」等等處理,最後才生成位元組碼檔案。

候選者:比如對泛型的擦除和我們經常用的Lombok就是在編譯階段乾的。

候選者:載入:將編譯後的class檔案載入到JVM中。

候選者:在載入階段又可以細化幾個步驟:裝載->連線->初始化

候選者:下面我對這些步驟又細說下哈。

候選者:【裝載時機】為了節省記憶體的開銷,並不會一次性把所有的類都裝載至JVM,而是等到「有需要」的時候才進行裝載(比如new和反射等等)

候選者:【裝載發生】class檔案是通過「類載入器」裝載到jvm中的,為了防止記憶體中出現多份同樣的位元組碼,使用了雙親委派機制(它不會自己去嘗試載入這個類,而是把請求委託給父載入器去完成,依次向上)

候選者:【裝載規則】JDK 中的本地方法類一般由根載入器(Bootstrp loader)裝載,JDK 中內部實現的擴充套件類一般由擴充套件載入器(ExtClassLoader )實現裝載,而程式中的類檔案則由系統載入器(AppClassLoader )實現裝載。

候選者:裝載這個階段它做的事情可以總結為:查詢並載入類的二進位制資料,在JVM「堆」中建立一個java.lang.Class類的物件,並將類相關的資訊儲存在JVM「方法區」中

面試官:嗯...

候選者:通過「裝載」這個步驟後,現在已經把class檔案裝載到JVM中了,並建立出對應的Class物件以及類資訊儲存至方法區了。

候選者:「連線」這個階段它做的事情可以總結為:對class的資訊進行驗證、為「類變數」分配記憶體空間並對其賦預設值。

候選者:連線又可以細化為幾個步驟:驗證->準備->解析

候選者:1. 驗證:驗證類是否符合 Java 規範和 JVM 規範

候選者:2. 準備:為類的靜態變數分配記憶體,初始化為系統的初始值

候選者:3. 解析:將符號引用轉為直接引用的過程

面試官:嗯...

候選者:通過「連線」這個步驟後,現在已經對class資訊做校驗並分配了記憶體空間和預設值了。

候選者:接下來就是「初始化」階段了,這個階段可以總結為:為類的靜態變數賦予正確的初始值。

候選者:過程大概就是收集class的靜態變數、靜態程式碼塊、靜態方法至()方法,隨後從上往下開始執行。

候選者:如果「例項化物件」則會呼叫方法對例項變數進行初始化,並執行對應的構造方法內的程式碼。

候選者:扯了這麼多,現在其實才完成至(編譯->載入->解釋->執行)中的載入階段,下面就來說下【解釋階段】做了什麼

候選者:初始化完成之後,當我們嘗試執行一個類的方法時,會找到對應方法的位元組碼的資訊,然後直譯器會把位元組碼資訊解釋成系統能識別的指令碼。

候選者:「解釋」這個階段它做的事情可以總結為:把位元組碼轉換為作業系統識別的指令

候選者:在解釋階段會有兩種方式把位元組碼資訊解釋成機器指令碼,一個是位元組碼直譯器、一個是即時編譯器(JIT)。

候選者:JVM會對「熱點程式碼」做編譯,非熱點程式碼直接進行解釋。當JVM發現某個方法或程式碼塊的執行特別頻繁的時候,就有可能把這部分程式碼認定為「熱點程式碼」

候選者:使用「熱點探測」來檢測是否為熱點程式碼。「熱點探測」一般有兩種方式,計數器和抽樣。HotSpot使用的是「計數器」的方式進行探測,為每個方法準備了兩類計數器:方法呼叫計數器和回邊計數器

候選者:這兩個計數器都有一個確定的閾值,當計數器超過閾值溢位了,就會觸發JIT編譯。

候選者:即時編譯器把熱點方法的指令碼儲存起來,下次執行的時候就無需重複的進行解釋,直接執行快取的機器語言

面試官:嗯...

候選者:解釋階段結束後,最後就到了執行階段。

候選者:「執行」這個階段它做的事情可以總結為:作業系統把直譯器解析出來的指令碼,呼叫系統的硬體執行最終的程式指令。

候選者:上面就是我對從原始碼檔案(.java)到程式碼執行的過程的理解了。

面試官:嗯...我還想問下你剛才提到的雙親委派模型...

候選者:下次一定!

本文總結:

  • Java跨平臺因為有JVM遮蔽了底層作業系統
  • Java原始碼到執行的過程,從JVM的角度看可以總結為四個步驟:編譯->載入->解釋->執行
    • 「編譯」經過 語法分析、語義分析、註解處理 最後才生成會class檔案
    • 「載入」又可以細分步驟為:裝載->連線->初始化。裝載則把class檔案裝載至JVM,連線則校驗class資訊、分配記憶體空間及賦預設值,初始化則為變數賦值為正確的初始值。連線裡又可以細化為:驗證、準備、解析
    • 「解釋」則是把位元組碼轉換成作業系統可識別的執行指令,在JVM中會有位元組碼直譯器和即時編譯器。在解釋時會對程式碼進行分析,檢視是否為「熱點程式碼」,如果為「熱點程式碼」則觸發JIT編譯,下次執行時就無需重複進行解釋,提高解釋速度
    • 「執行」呼叫系統的硬體執行最終的程式指令

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!

面試官:Java從編譯到執行,發生了什麼?

【對線面試官-移動端】系列 一週兩篇持續更新中!

【對線面試官-電腦端】系列 一週兩篇持續更新中!

原創不易!!求三連!!

相關文章