阿里、美團、Oracle等大廠的Java虛擬機器面試題集錦
“有程式碼的地方,就有江湖。”
程式設計師,就是“一人,一鍵,二機”行走其間的孤獨劍客。我們遊走程式碼江湖,彈指間,便可掀起一場風雨變革。而在江湖中狂蕩,必然要練就絕世武功,則需要內外兼備:精妙的招式,加之深厚的內功。武功的基礎是內功,一個內功低的人招式再奇妙,也打不過一個內功深厚之人。同樣兩者也是相輔相成,內功深厚,原來的一招一式威力也會倍增。
對於開發者來說,其道理也是一樣。流行的框架越來越多,封裝也越來越完善,各種框架可以搞定一切。初級程式設計師只要熟悉基本的使用方法,幾乎不用關注底層的實現,便可以快速地開發上線。但對於想要進階的你來說,更要注重內功,比如演算法、設計模式、底層原理等等。只有把基礎打紮實,才能知其然知其所以然,出現Bug能快速發現問題本質。
我在Java虛擬機器效能優化方面有著多年的研究,深知Spring全家桶是精妙的招式,JVM就是內功心法很重要的一塊。線上出現效能問題,JVM調優更是不可迴避的問題。但又因Java虛擬機器封裝得太好,讓我們幾乎感覺不到它的存在,可學習Java虛擬機器對於高階程式設計師來說,其重要性是不言而喻的。我司在面試高階開發的時候,JVM相關知識也必定是考核的標準之一。
下面這篇文章集錦了阿里、美團、Oracle等大廠的JVM考點,你看看是否會能答得上來?
-
什麼是Java虛擬機器?為什麼Java被稱作是“平臺無關的程式語言”?
-
Java程式碼是怎麼執行的?
-
Java虛擬機器是如何載入Java類的?
-
JVM執行記憶體的分類
-
如何監控和診斷JVM堆內和堆外記憶體使用?
-
Java四引用是什麼?
-
如何理解JVM內建的編譯或GC日誌?
-
JVM的永久代中會發生垃圾回收麼?
-
Java中的兩種異常型別是什麼?他們有什麼區別?
-
JVM是如何實現同步的?
-
Java內在模型是什麼?
-
即使編譯器有哪些優化?
-
在什麼情況下重複讀寫操作會被優化?
-
什麼樣的垃圾才被回收?
-
什麼時候會導致垃圾回收?
-
如何利用JFR和JMC監控Java程式?
-
如何利用Unsafe API 繞開 JVM的控制?
-
如何利用位元組碼注入為已有程式碼加料?
……
根據我專欄的內容,我挑選了幾個問題進行解答,希望能對大家面試起到幫助。
1、什麼是Java虛擬機器?為什麼Java被稱作是“平臺無關的程式語言”?
Java虛擬機器是一個可以執行Java位元組碼的虛擬機器程序。Java原始檔被編譯成能被Java虛擬機器執行的位元組碼檔案。
Java被設計成允許應用程式可以執行在任意的平臺,而不需要程式設計師為每一個平臺單獨重寫或者是重新編譯。Java虛擬機器讓這個變為可能,因為它知道底層硬體平臺的指令長度和其他特性。
2、Java程式碼是怎麼執行的?
這個問題可以分三塊來回答:
-
為什麼Java要在虛擬機器裡執行?
-
Java虛擬機器具體是怎樣執行Java位元組碼的?
-
Java虛擬機器的執行效率究竟是怎麼樣的?
Java之所以要在虛擬機器中執行,是因為它提供了可移植性。一旦Java程式碼被編譯為Java位元組碼,便可以在不同平臺上的Java虛擬機器實現上執行。此外,虛擬機器還提供了一個程式碼託管的環境,代替我們處理部分冗長而且容易出錯的事務,例如記憶體管理。
Java虛擬機器將執行時記憶體區域劃分為五個部分,分別為方法區、堆、PC暫存器、Java方法棧和本地方法棧。Java程式編譯而成的class檔案,需要先載入至方法區中,方能在Java虛擬機器中執行。
為了提高執行效率,標準JDK中的HotSpot虛擬機器採用的是一種混合執行的策略。首先,它會解釋執行Java位元組碼,然後會將其中反覆執行的熱點程式碼,以方法為單位進行即時編譯,翻譯成機器碼後直接執行在底層硬體之上。HotSpot裝載了多個不同的即時編譯器,以便在編譯時間和生成程式碼的執行效率之間做取捨。
3、Java虛擬機器是如何載入Java類的?
Java虛擬機器將位元組流轉化為Java類的過程,可分為載入、連結以及初始化三大步驟。也可以用蓋房子來類比Java虛擬機器中的類載入。
載入是指查詢位元組流,並且據此建立類的過程。以蓋房子為例,村裡的Tony要蓋個房子,那麼按照流程他得先找個建築師,跟他說想要設計一個房型,比如說“一房、一廳、四衛”。這裡的房型相當於類,而建築師,就相當於類載入器。村裡有許多建築師,他們等級森嚴,但有著共同的祖師爺,叫啟動類載入器(boot class loader)。
載入需要藉助類載入器,在Java虛擬機器中,類載入器使用了雙親委派模型,即接收到載入請求時,會先將請求轉發給父類載入器。
連結,是指將建立成的類合併至Java虛擬機器中,使之能夠執行的過程。連結還分驗證、準備和解析三個階段。其中,解析階段為非必須的。
初始化,則是為標記為常量值的欄位賦值,以及執行<clinit>方法的過程。類的初始化僅會被執行一次,這個特性被用來實現單例的延遲初始化。這放在我們蓋房子的例子中就是,只有當房子裝修過後,Tony才能真正地住進去。
想了解更多JVM內容,可訂閱我的《深入拆解Java虛擬機器》專欄,已全部完結,一次性就可學習看完。
4、如何監控和診斷JVM堆內和堆外記憶體使用?
瞭解 JVM 記憶體的方法有很多,具體能力範圍也有區別,簡單總結如下:
可以使用綜合性的圖形化工具,如 JConsole、VisualVM(注意,從 Oracle JDK 9 開始,VisualVM 已經不再包含在 JDK 安裝包中)等。這些工具具體使用起來相對比較直觀,直接連線到 Java 程序,然後就可以在圖形化介面裡掌握記憶體使用情況。
以 JConsole 為例,其記憶體頁面可以顯示常見的堆記憶體和各種堆外部分使用狀態。
也可以使用命令列工具進行執行時查詢,如 jstat 和 jmap 等工具都提供了一些選項,可以檢視堆、方法區等使用資料。
或者,也可以使用 jmap 等提供的命令,生成堆轉儲(Heap Dump)檔案,然後利用 jhat 或 Eclipse MAT 等堆轉儲分析工具進行詳細分析。
如果你使用的是 Tomcat、Weblogic 等 Java EE 伺服器,這些伺服器同樣提供了記憶體管理相關的功能。
另外,從某種程度上來說,GC 日誌等輸出,同樣包含著豐富的資訊。
這裡有一個相對特殊的部分,就是是堆外記憶體中的直接記憶體,前面的工具基本不適用,可以使用 JDK 自帶的 Native Memory Tracking(NMT)特性,它會從 JVM 本地記憶體分配的角度進行解讀。
5、JVM的永久代中會發生垃圾回收麼?
垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full GC)。如果你仔細檢視垃圾收集器的輸出資訊,就會發現永久代也是被回收的。這就是為什麼正確的永久代大小對避免Full GC是非常重要的原因。
(注:Java8中已經移除了永久代,新加了一個叫做元資料區的native記憶體區) 異常處理
6、在Java中,物件什麼時候可以被垃圾回收?
當物件對當前使用這個物件的應用程式變得不可觸及的時候,這個物件就可以被回收了。
7、Java中的兩種異常型別是什麼?他們有什麼區別?
Java中有兩種異常:受檢查的(checked)異常和不受檢查的(unchecked)異常。不受檢查的異常不需要在方法或者是建構函式上宣告,就算方法或者是建構函式的執行可能會丟擲這樣的異常,並且不受檢查的異常可以傳播到方法或者是建構函式的外面。相反,受檢查的異常必須要用throws語句在方法或者是建構函式上宣告。這裡有Java異常處理的一些小建議。
8、JVM垃圾回收演算法
標記-清除演算法:首先標記出所有需要回收的物件,在標記完成後統一回收所有被標記的物件。
複製演算法:將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當一塊記憶體用完了,將還存另外一塊上面,然後在把已使用過的記憶體空間一次清理掉。
標記-整理演算法:標記過程與“標記-清除”演算法一樣,但後續步驟不是直接對可回收物件進行清理,而是讓所一端移動,然後直接清理掉端邊界以外的記憶體。
分代收集演算法:一般是把Java堆分為新生代和老年代,根據各個年代的特點採用最適當的收集演算法。新生代都發現有大批物件死去,選用複製演算法。老年代中因為物件存活率高,必須使用“標記-清理”或“標記-整理”演算法來進行回收。
專欄雖然到此已經結束了,但是並不代表你對Java虛擬機器學習的停止。我想,專欄的內容僅僅是為你打開了JVM學習的大門,裡面的風景,還是需要你自己來探索。在文章的後面,我列出了一系列的Java虛擬機器技術的相關部落格和閱讀資料,你仍然可以繼續加餐。
你可以關注國內幾位Java虛擬機器大咖的微信公眾號:
-
R大,個人認為是中文圈子裡最瞭解Java虛擬機器設計實現的人,你可以關注他的[知乎賬號](https://www.zhihu.com/people/rednaxelafx);
-
[你假笨](https://open.weixin.qq.com/qr/code?username=lovestblog),原阿里Java虛擬機器團隊成員,現[PerfMa](http://www.perfma.com/) CEO;
-
[江南白衣](https://open.weixin.qq.com/qr/code?username=jnby1978),唯品會資深架構師;
-
[佔小狼](https://open.weixin.qq.com/qr/code?username=whywhy_zj),美團基礎架構部技術專家;
-
[楊曉峰](https://open.weixin.qq.com/qr/code?username=gh_9f3b2a4e2a74),前甲骨文首席工程師。
如果英文閱讀沒問題的話,你可以關注[Cliff Click](http://cliffc.org/blog/)、[Aleksey Shipilëv](https://shipilev.net/)(他的[JVM Anatomy Park](https://shipilev.net/jvm-anatomy-park/)十分有趣)和[Nitsan Wakart](http://psy-lob-saw.blogspot.com/)的部落格。
你也可以關注[Java Virtual Machine Language Submit](http://openjdk.java.net/projects/mlvm/jvmlangsummit/)和[Oracle Code One](https://www.oracle.com/code-one/index.html)(前身是JavaOne大會)中關於Java虛擬機器的演講,以便掌握Java的最新發展動向。
至於其他閱讀材料,你可以參考R大的這份[書單](https://www.douban.com/doulist/2545443/),或者這個[彙總貼](https://github.com/deephacks/awesome-jvm)。
道阻且長,努力加餐~!
可以說,Java虛擬機器就是每一位Java工程師進階加薪的利器,你想往上升,你想深入技術,不想一直停留在簡單開發,或者你在做Java效能分析、調優工作時,那麼,Java虛擬機器絕對是一把助力的利劍。