從你接觸 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 提供的 Hotspot 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.class
jaotc --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](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內功
- 模塊一 Java基礎
- 第1講 談談你對Java平臺的理解?
- 第2講 Exception和Error有什么區別?
- 第3講 談談final、finally、 finalize有什么不同?
- 第4講 強引用、軟引用、弱引用、幻象引用有什么區別?
- 第5講 String、StringBuffer、StringBuilder有什么區別?
- 第6講 動態代理是基于什么原理?
- 第7講 int和Integer有什么區別?
- 第8講 對比Vector、ArrayList、LinkedList有何區別?
- 第9講 對比Hashtable、HashMap、TreeMap有什么不同?
- 第10講 如何保證集合是線程安全的? ConcurrentHashMap如何實現高效地線程安全?
- 第11講 Java提供了哪些IO方式? NIO如何實現多路復用?
- 第12講 Java有幾種文件拷貝方式?哪一種最高效?
- 第13講 談談接口和抽象類有什么區別?
- 第14講 談談你知道的設計模式?
- 模塊二 Java進階
- 第15講 synchronized和ReentrantLock有什么區別呢?
- 第16講 synchronized底層如何實現?什么是鎖的升級、降級?
- 第17講 一個線程兩次調用start()方法會出現什么情況?
- 第18講 什么情況下Java程序會產生死鎖?如何定位、修復?
- 第19講 Java并發包提供了哪些并發工具類?
- 第20講 并發包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么區別?
- 第21講 Java并發類庫提供的線程池有哪幾種? 分別有什么特點?
- 第22講 AtomicInteger底層實現原理是什么?如何在自己的產品代碼中應用CAS操作?
- 第23講 請介紹類加載過程,什么是雙親委派模型?
- 第24講 有哪些方法可以在運行時動態生成一個Java類?
- 第25講 談談JVM內存區域的劃分,哪些區域可能發生OutOfMemoryError?
- 第26講 如何監控和診斷JVM堆內和堆外內存使用?
- 第27講 Java常見的垃圾收集器有哪些?
- 第28講 談談你的GC調優思路?
- 第29講 Java內存模型中的happen-before是什么?
- 第30講 Java程序運行在Docker等容器環境有哪些新問題?
- 模塊三 Java安全基礎
- 第31講 你了解Java應用開發中的注入攻擊嗎?
- 第32講 如何寫出安全的Java代碼?
- 模塊四 Java性能基礎
- 第33講 后臺服務出現明顯“變慢”,談談你的診斷思路?
- 第34講 有人說“Lambda能讓Java程序慢30倍”,你怎么看?
- 第35講 JVM優化Java代碼時都做了什么?
- 模塊五 Java應用開發擴展
- 第36講 談談MySQL支持的事務隔離級別,以及悲觀鎖和樂觀鎖的原理和應用場景?
- 第37講 談談Spring Bean的生命周期和作用域?
- 第38講 對比Java標準NIO類庫,你知道Netty是如何實現更高性能的嗎?
- 第39講 談談常用的分布式ID的設計方案?Snowflake是否受冬令時切換影響?
- 周末福利
- 周末福利 談談我對Java學習和面試的看法
- 周末福利 一份Java工程師必讀書單
- 結束語
- 結束語 技術沒有終點
- 結課測試 Java核心技術的這些知識,你真的掌握了嗎?