從你接觸 Java 開發到現在,你對 Java 最直觀的印象是什麼呢?是它宣傳的 「Write once, run anywhere」,還是目前看已經有些過於形式主義的語法呢?你對於 Java 平臺到底了解到什麼程度?請你先停下來總結思考一下。
今天我要問你的問題是,談談你對 Java 平臺的理解?「Java 是解釋執行」,這句話正確嗎?
Java 本身是一種面向對象的語言,最顯著的特性有兩個方面,一是所謂的「書寫一次,到處運行」(Write once, run anywhere),能夠非常容易地獲得跨平臺能力;另外就是垃圾收集(GC, Garbage Collection),Java 通過垃圾收集器(Garbage Collector)回收分配內存,大部分情況下,程式設計師不需要自己操心內存的分配和回收。
我們日常會接觸到 JRE(Java Runtime Environment)或者 JDK(Java Development Kit)。 JRE,也就是 Java 運行環境,包含了 JVM 和 Java 類庫,以及一些模塊等。而 JDK 可以看作是 JRE 的一個超集,提供了更多工具,比如編譯器、各種診斷工具等。
對於「Java 是解釋執行」這句話,這個說法不太準確。我們開發的 Java 的原始碼,首先通過 Javac 編譯成為字節碼(bytecode),然後,在運行時,通過 Java 虛擬機(JVM)內嵌的解釋器將字節碼轉換成為最終的機器碼。但是常見的 JVM,比如我們大多數情況使用的 Oracle JDK 提供的 Hospot JVM,都提供了 JIT(Just-In-Time)編譯器,也就是通常所說的動態編譯器,JIT 能夠在運行時將熱點代碼編譯成機器碼,這種情況下部分熱點代碼就屬於編譯執行,而不是解釋執行了。
其實這個問題,問得有點籠統。題目本身是非常開放的,往往考察的是多個方面,比如,基礎知識理解是否很清楚;是否掌握 Java 平臺主要模塊和運行原理等。很多面試者會在這種問題上吃虧,稍微緊張了一下,不知道從何說起,就給出個很簡略的回答。
對於這類籠統的問題,你需要儘量表現出自己的思維深入並系統化,Java 知識理解得也比較全面,一定要避免讓面試官覺得你是個「知其然不知其所以然」的人。畢竟明白基本組成和機制,是日常工作中進行問題診斷或者性能調優等很多事情的基礎,相信沒有招聘方會不喜歡「熱愛學習和思考」的面試者。
即使感覺自己的回答不是非常完善,也不用擔心。我個人覺得這種籠統的問題,有時候回答得稍微片面也很正常,大多數有經驗的面試官,不會因為一道題就對面試者輕易地下結論。通常會儘量引導面試者,把他的真實水平展現出來,這種問題就是做個開場熱身,面試官經常會根據你的回答擴展相關問題。
回歸正題,對於 Java 平臺的理解,可以從很多方面簡明扼要地談一下,例如:Java 語言特性,包括泛型、Lambda 等語言特性;基礎類庫,包括集合、IO/NIO、網絡、並發、安全等基礎類庫。對於我們日常工作應用較多的類庫,面試前可以系統化總結一下,有助於臨場發揮。
或者談談 JVM 的一些基礎概念和機制,比如 Java 的類加載機制,常用版本 JDK(如 JDK 8)內嵌的 Class-Loader,例如 Bootstrap、 Application 和 Extension Class-loader;類加載大致過程:加載、驗證、連結、初始化(這裡參考了周志明的《深入理解 Java 虛擬機》,非常棒的 JVM 上手書籍);自定義 Class-Loader 等。還有垃圾收集的基本原理,最常見的垃圾收集器,如 SerialGC、Parallel GC、 CMS、 G1 等,對於適用於什麼樣的工作負載最好也心裡有數。這些都是可以擴展開的領域,我會在後面的專欄對此進行更系統的介紹。
當然還有 JDK 包含哪些工具或者 Java 領域內其他工具等,如編譯器、運行時環境、安全工具、診斷和監控工具等。這些基本工具是日常工作效率的保證,對於我們工作在其他語言平臺上,同樣有所幫助,很多都是觸類旁通的。
下圖是我總結的一個相對寬泛的藍圖供你參考。
不再擴展了,回到前面問到的解釋執行和編譯執行的問題。有些面試官喜歡在特定問題上「刨根問底兒」,因為這是進一步了解面試者對知識掌握程度的有效方法,我稍微深入探討一下。
眾所周知,我們通常把 Java 分為編譯期和運行時。這裡說的 Java 的編譯和 C/C++ 是有著不同的意義的,Javac 的編譯,編譯 Java 源碼生成「.class」文件裡面實際是字節碼,而不是可以直接執行的機器碼。Java 通過字節碼和 Java 虛擬機(JVM)這種跨平臺的抽象,屏蔽了作業系統和硬體的細節,這也是實現「一次編譯,到處執行」的基礎。
在運行時,JVM 會通過類加載器(Class-Loader)加載字節碼,解釋或者編譯執行。就像我前面提到的,主流 Java 版本中,如 JDK 8 實際是解釋和編譯混合的一種模式,即所謂的混合模式(-Xmixed)。通常運行在 server 模式的 JVM,會進行上萬次調用以收集足夠的信息進行高效的編譯,client 模式這個門限是 1500 次。Oracle Hotspot JVM 內置了兩個不同的 JIT compiler,C1 對應前面說的 client 模式,適用於對於啟動速度敏感的應用,比如普通 Java 桌面應用;C2 對應 server 模式,它的優化是為長時間運行的伺服器端應用設計的。默認是採用所謂的分層編譯(TieredCompilation)。這裡不再展開更多 JIT 的細節,沒必要一下子就鑽進去,我會在後面介紹分層編譯的內容。
Java 虛擬機啟動時,可以指定不同的參數對運行模式進行選擇。 比如,指定「-Xint」,就是告訴 JVM 只進行解釋執行,不對代碼進行編譯,這種模式拋棄了 JIT 可能帶來的性能優勢。畢竟解釋器(interpreter)是逐條讀入,逐條解釋運行的。與其相對應的,還有一個「-Xcomp」參數,這是告訴 JVM 關閉解釋器,不要進行解釋執行,或者叫作最大優化級別。那你可能會問這種模式是不是最高效啊?簡單說,還真未必。「-Xcomp」會導致 JVM 啟動變慢非常多,同時有些 JIT 編譯器優化方式,比如分支預測,如果不進行 profiling,往往並不能進行有效優化。
除了我們日常最常見的 Java 使用模式,其實還有一種新的編譯方式,即所謂的 AOT(Ahead-of-Time Compilation),直接將字節碼編譯成機器代碼,這樣就避免了 JIT 預熱等各方面的開銷,比如 Oracle JDK 9 就引入了實驗性的 AOT 特性,並且增加了新的 jaotc 工具。利用下面的命令把某個類或者某個模塊編譯成為 AOT 庫。
jaotc --output libHelloWorld.so HelloWorld.classjaotc --output libjava.base.so --module java.base
然後,在啟動時直接指定就可以了。
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld
而且,Oracle JDK 支持分層編譯和 AOT 協作使用,這兩者並不是二選一的關係。如果你有興趣,可以參考相關文檔:http://openjdk.java.net/jeps/295。AOT 也不僅僅是只有這一種方式,業界早就有第三方工具(如 GCJ、Excelsior JET)提供相關功能。
另外,JVM 作為一個強大的平臺,不僅僅只有 Java 語言可以運行在 JVM 上,本質上合規的字節碼都可以運行,Java 語言自身也為此提供了便利,我們可以看到類似 Clojure、Scala、Groovy、JRuby、Jython 等大量 JVM 語言,活躍在不同的場景。
今天,我簡單介紹了一下 Java 平臺相關的一些內容,目的是提綱挈領地構建一個整體的印象,包括 Java 語言特性、 核心類庫與常用第三方類庫、Java 虛擬機基本原理和相關工具,希望對你有所幫助。
想看更多 Java 經典面試題深度解析?推薦關注《Java 核心技術 36 講》。專欄重點圍繞「術」與「道」,為你講解 Java 面試的核心知識點。就算你暫時不需要準備面試,照樣可以通過這個專欄,提升 Java 技能。
福利一:限時優惠價¥58 最後一天,原價¥68(1 月 12 日恢復原價)
福利二:每邀請一位好友購買,你可獲得 18 元現金返現,多邀多得,上不封頂,立即提現
長按識別下圖二維碼,立即訂閱。