Java面試中,遇到這類面試題最吃虧!
從你接觸 Java開(kāi)發(fā)到現(xiàn)在,你對(duì) Java最直觀的印象是什么呢?是它宣傳的 “Compile once, run anywhere”,還是目前看已經(jīng)有些過(guò)于形式主義的語(yǔ)法呢?你對(duì)于 Java平臺(tái)到底了解到什么程度?請(qǐng)你先停下來(lái)總結(jié)思考一下。
今天要問(wèn)你的問(wèn)題是,談?wù)勀銓?duì) Java平臺(tái)的理解?“Java是解釋執(zhí)行”,這句話正確嗎?
典型回答
Java本身是一種面向?qū)ο蟮恼Z(yǔ)言,最顯著的特性有兩個(gè)方面:
一是所謂的“一次編譯,到處執(zhí)行”(Compile once, run anywhere),能夠非常容易地獲得跨平臺(tái)能力;
另外就是垃圾收集(GC, Garbage Collection),Java通過(guò)垃圾收集器(Garbage Collector)回收分配內(nèi)存,大部分情況下,程序員不需要自己操心內(nèi)存的分配和回收。
我們?nèi)粘?huì)接觸到 JRE(Java Runtime Environment)或者 JDK(Java Development Kit)。 JRE,也就是 Java運(yùn)行環(huán)境,包含了 JVM和 Java類庫(kù),以及一些模塊等。而 JDK可以看作是 JRE的一個(gè)超集,提供了更多工具,比如編譯器、各種診斷工具等。
對(duì)于“Java是解釋執(zhí)行”這句話,這個(gè)說(shuō)法不太準(zhǔn)確。
我們開(kāi)發(fā)的 Java的源代碼,首先通過(guò) Javac編譯成為字節(jié)碼(bytecode),然后,在運(yùn)行時(shí),通過(guò) Java虛擬機(jī)(JVM)內(nèi)嵌的解釋器將字節(jié)碼轉(zhuǎn)換成為最終的機(jī)器碼。但是常見(jiàn)的 JVM,比如我們大多數(shù)情況使用的 Oracle JDK提供的 Hospot JVM,都提供了 JIT(Just-In-Time)編譯器,也就是通常所說(shuō)的動(dòng)態(tài)編譯器,JIT能夠在運(yùn)行時(shí)將熱點(diǎn)代碼編譯成機(jī)器碼,這種情況下部分熱點(diǎn)代碼就屬于編譯執(zhí)行,而不是解釋執(zhí)行了。
考點(diǎn)分析
其實(shí)這個(gè)問(wèn)題,問(wèn)得有點(diǎn)籠統(tǒng)。題目本身是非常開(kāi)放的,往往考察的是多個(gè)方面,比如,基礎(chǔ)知識(shí)理解是否很清楚;是否掌握 Java平臺(tái)主要模塊和運(yùn)行原理等。很多面試者會(huì)在這種問(wèn)題上吃虧,稍微緊張了一下,不知道從何說(shuō)起,就給出個(gè)很簡(jiǎn)略的回答。
對(duì)于這類籠統(tǒng)的問(wèn)題,你需要盡量表現(xiàn)出自己的思維深入并系統(tǒng)化,Java知識(shí)理解得也比較全面,一定要避免讓面試官覺(jué)得你是個(gè)“知其然不知其所以然”的人。畢竟明白基本組成和機(jī)制,是日常工作中進(jìn)行問(wèn)題診斷或者性能調(diào)優(yōu)等很多事情的基礎(chǔ),相信沒(méi)有招聘方會(huì)不喜歡“熱愛(ài)學(xué)習(xí)和思考”的面試者。
即使感覺(jué)自己的回答不是非常完善,也不用擔(dān)心。我個(gè)人覺(jué)得這種籠統(tǒng)的問(wèn)題,有時(shí)候回答得稍微片面也很正常,大多數(shù)有經(jīng)驗(yàn)的面試官,不會(huì)因?yàn)橐坏李}就對(duì)面試者輕易地下結(jié)論。通常會(huì)盡量引導(dǎo)面試者,把他的真實(shí)水平展現(xiàn)出來(lái),這種問(wèn)題就是做個(gè)開(kāi)場(chǎng)熱身,面試官經(jīng)常會(huì)根據(jù)你的回答擴(kuò)展相關(guān)問(wèn)題。
知識(shí)擴(kuò)展
回歸正題,對(duì)于 Java平臺(tái)的理解,可以從很多方面簡(jiǎn)明扼要地談一下,例如:Java語(yǔ)言特性,包括泛型、Lambda等語(yǔ)言特性;基礎(chǔ)類庫(kù),包括集合、IO/NIO、網(wǎng)絡(luò)、并發(fā)、安全等基礎(chǔ)類庫(kù)。對(duì)于我們?nèi)粘9ぷ鲬?yīng)用較多的類庫(kù),面試前可以系統(tǒng)化總結(jié)一下,有助于臨場(chǎng)發(fā)揮。
或者談?wù)?JVM的一些基礎(chǔ)概念和機(jī)制,比如 Java的類加載機(jī)制,常用版本 JDK(如 JDK 8)內(nèi)嵌的 Class-Loader,例如 Bootstrap、 Application和 Extension Class-loader;類加載大致過(guò)程:加載、驗(yàn)證、鏈接、初始化(這里參考了周志明的《深入理解 Java虛擬機(jī)》,非常棒的 JVM上手書籍);自定義 Class-Loader等。還有垃圾收集的基本原理,最常見(jiàn)的垃圾收集器,如 SerialGC、Parallel GC、 CMS、 G1等,對(duì)于適用于什么樣的工作負(fù)載***也心里有數(shù)。這些都是可以擴(kuò)展開(kāi)的領(lǐng)域,我會(huì)在后面的專欄對(duì)此進(jìn)行更系統(tǒng)的介紹。
當(dāng)然還有 JDK包含哪些工具或者 Java領(lǐng)域內(nèi)其他工具等,如編譯器、運(yùn)行時(shí)環(huán)境、安全工具、診斷和監(jiān)控工具等。這些基本工具是日常工作效率的保證,對(duì)于我們工作在其他語(yǔ)言平臺(tái)上,同樣有所幫助,很多都是觸類旁通的。
下圖是我總結(jié)的一個(gè)相對(duì)寬泛的藍(lán)圖供你參考。
不再擴(kuò)展了,回到前面問(wèn)到的解釋執(zhí)行和編譯執(zhí)行的問(wèn)題。有些面試官喜歡在特定問(wèn)題上“刨根問(wèn)底兒”,因?yàn)檫@是進(jìn)一步了解面試者對(duì)知識(shí)掌握程度的有效方法,我稍微深入探討一下。
眾所周知,我們通常把 Java分為編譯期和運(yùn)行時(shí)。這里說(shuō)的 Java的編譯和 C/C++是有著不同的意義的,Javac的編譯,編譯 Java源碼生成“.class”文件里面實(shí)際是字節(jié)碼,而不是可以直接執(zhí)行的機(jī)器碼。Java通過(guò)字節(jié)碼和 Java虛擬機(jī)(JVM)這種跨平臺(tái)的抽象,屏蔽了操作系統(tǒng)和硬件的細(xì)節(jié),這也是實(shí)現(xiàn)“一次編譯,到處執(zhí)行”的基礎(chǔ)。
在運(yùn)行時(shí),JVM會(huì)通過(guò)類加載器(Class-Loader)加載字節(jié)碼,解釋或者編譯執(zhí)行。就像我前面提到的,主流 Java版本中,如 JDK 8實(shí)際是解釋和編譯混合的一種模式,即所謂的混合模式(-Xmixed)。通常運(yùn)行在 server模式的 JVM,會(huì)進(jìn)行上萬(wàn)次調(diào)用以收集足夠的信息進(jìn)行高效的編譯,client模式這個(gè)門限是 1500次。Oracle Hotspot JVM內(nèi)置了兩個(gè)不同的 JIT compiler,C1對(duì)應(yīng)前面說(shuō)的 client模式,適用于對(duì)于啟動(dòng)速度敏感的應(yīng)用,比如普通 Java桌面應(yīng)用;C2對(duì)應(yīng) server模式,它的優(yōu)化是為長(zhǎng)時(shí)間運(yùn)行的服務(wù)器端應(yīng)用設(shè)計(jì)的。默認(rèn)是采用所謂的分層編譯(TieredCompilation)。這里不再展開(kāi)更多 JIT的細(xì)節(jié),沒(méi)必要一下子就鉆進(jìn)去,我會(huì)在后面介紹分層編譯的內(nèi)容。
Java虛擬機(jī)啟動(dòng)時(shí),可以指定不同的參數(shù)對(duì)運(yùn)行模式進(jìn)行選擇。 比如,指定“-Xint”,就是告訴 JVM只進(jìn)行解釋執(zhí)行,不對(duì)代碼進(jìn)行編譯,這種模式拋棄了 JIT可能帶來(lái)的性能優(yōu)勢(shì)。畢竟解釋器(interpreter)是逐條讀入,逐條解釋運(yùn)行的。與其相對(duì)應(yīng)的,還有一個(gè)“-Xcomp”參數(shù),這是告訴 JVM關(guān)閉解釋器,不要進(jìn)行解釋執(zhí)行,或者叫作***優(yōu)化級(jí)別。那你可能會(huì)問(wèn)這種模式是不是***效啊?簡(jiǎn)單說(shuō),還真未必。“-Xcomp”會(huì)導(dǎo)致 JVM啟動(dòng)變慢非常多,同時(shí)有些 JIT編譯器優(yōu)化方式,比如分支預(yù)測(cè),如果不進(jìn)行 profiling,往往并不能進(jìn)行有效優(yōu)化。
除了我們?nèi)粘W畛R?jiàn)的 Java使用模式,其實(shí)還有一種新的編譯方式,即所謂的 AOT(Ahead-of-Time Compilation),直接將字節(jié)碼編譯成機(jī)器代碼,這樣就避免了 JIT預(yù)熱等各方面的開(kāi)銷,比如 Oracle JDK 9就引入了實(shí)驗(yàn)性的 AOT特性,并且增加了新的 jaotc工具。利用下面的命令把某個(gè)類或者某個(gè)模塊編譯成為 AOT庫(kù)。
- jaotc --output libHelloWorld.so HelloWorld.class
- jaotc --output libjava.base.so --module java.base
然后,在啟動(dòng)時(shí)直接指定就可以了。
- java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld
而且,Oracle JDK支持分層編譯和 AOT協(xié)作使用,這兩者并不是二選一的關(guān)系。如果你有興趣,可以參考相關(guān)文檔:http://openjdk.java.net/jeps/29***OT也不僅僅是只有這一種方式,業(yè)界早就有第三方工具(如 GCJ、Excelsior JET)提供相關(guān)功能。
另外,JVM作為一個(gè)強(qiáng)大的平臺(tái),不僅僅只有 Java語(yǔ)言可以運(yùn)行在 JVM上,本質(zhì)上合規(guī)的字節(jié)碼都可以運(yùn)行,Java語(yǔ)言自身也為此提供了便利,我們可以看到類似 Clojure、Scala、Groovy、JRuby、Jython等大量 JVM語(yǔ)言,活躍在不同的場(chǎng)景。
今天,我簡(jiǎn)單介紹了一下 Java平臺(tái)相關(guān)的一些內(nèi)容,目的是提綱挈領(lǐng)地構(gòu)建一個(gè)整體的印象,包括 Java語(yǔ)言特性、 核心類庫(kù)與常用第三方類庫(kù)、Java虛擬機(jī)基本原理和相關(guān)工具,希望對(duì)你有所幫助。