面試官:今天從基礎先問起吧,你是怎么理解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面試,對線面試官系列持續更新中!

【對線面試官-移動端】系列 一周兩篇持續更新中!
【對線面試官-電腦端】系列 一周兩篇持續更新中!
原創不易!!求三連!!