95%的技術面試必考的JVM知識點都在這,另附加分思路!

Java_老男孩發表於2019-04-28

概述:知識點彙總

jvm的知識點彙總共6個大方向:記憶體模型、類載入機制、GC垃圾回收是比較重點的內容。效能調優部分偏重實際應用,重點突出實踐能力。編譯器優化和執行模式部分偏重理論基礎,主要掌握知識點。

各個部分的內容如下:

1>記憶體模型部分:程式計數器、方法區、堆、棧、本地方法棧的作用,儲存哪些資料;

2>類載入部分:雙親委派的載入機制以及常用類載入器分別載入哪種型別的類;

3>GC部分:分代回收的思想和依據,以及不同垃圾回收演算法實現的思路、適合的場景;

4>效能調優部分:常用的jvm優化引數的作用,引數調優的依據,要了解常用的jvm分析工具能分析哪類問題,以及使用方法;

5>執行模式部分:解釋、編譯、混合模式的優缺點,瞭解java7提供的分層編譯技術。需要知道JIT即時編譯技術和OSR也就是棧上替換,知道C1、C2編譯器針對的場景,其中C2針對server模式,優化更激進。在新技術方面可以瞭解一下java10提供的由java實現的graal編譯器。

6>編譯優化部分:前端編譯器javac的編譯過程、AST抽象語法樹、編譯期優化和執行期優化。編譯優化的常用技術,包括公共子表示式的消除、方法內聯、逃逸分析、棧上分配、同步消除等。明白了這些才能寫出對編譯器友好的程式碼。

jvm的內容相對來說比較集中,但是對知識深度的掌握要求較高,建議面試前重點加強。

一、jvm記憶體相關考點

1.詳解-jvm記憶體模型

jvm記憶體模型主要指執行時的資料區,包括5個部分。

棧也叫方法棧,是執行緒私有的,執行緒在執行每個方法時都會同時建立一個棧幀,用來儲存區域性變數表、操作棧、動態連結、方法出口等資訊。呼叫方法時執行入棧,方法返回時執行出棧。

本地方法棧與棧類似,也是用來儲存執行緒執行方法時的資訊,不同的是,執行java方法使用棧,而執行native方法使用本地方法棧。

程式計數器儲存著當前執行緒所執行的位元組碼位置,每個執行緒工作時都有一個獨立的計數器。程式計數器為執行java方法服務,執行native方法時,程式計數器為空。

棧、本地方法棧、程式計數器這三個部分都是執行緒獨佔的。

堆是jvm管理的記憶體中最大的一塊,堆被所有執行緒共享,目的是為了存放物件例項,幾乎所有的物件例項都在這裡分配。當堆記憶體沒有可用的空間時,會丟擲OOM異常。根據物件存活的週期不同,jvm把堆記憶體進行分代管理,由垃圾回收器來進行物件的回收管理。

方法區也是各個執行緒共享的記憶體區域,又叫非堆區。用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料,

jdk1.7中的永久代和1.8中的metaspace都是方法區的一種實現。

面試回答此知識點相關問題時,要答出兩個要點:一個是各部分的功能,另一個是哪些執行緒共享,哪些獨佔。

2.詳解-jmm記憶體可見性

jmm是java記憶體模型,與剛才講到的jvm記憶體模型是兩回事,jmm的主要目標是定義程式中變數的訪問規則,如圖所示,所有的共享變數都儲存在主記憶體中共享。每個執行緒有自己的工作記憶體,工作記憶體中儲存的是主記憶體中變數的副本,執行緒對變數的讀寫等操作必須在自己 的工作記憶體中進行,而不能直接讀寫主記憶體中的變數。

在多執行緒進行資料互動時,例如執行緒a給一個共享變數賦值後,由執行緒b來讀取這個值,a修改完變數是修改在自己的工作區記憶體中,b是不可見的,只有從a的工作區寫回到主記憶體,b再從主記憶體讀取到自己的工作區才能進行進一步的操作。由於指令重排序的存在,這個寫-讀的順序有可能被打亂。

因此jmm需要提供原子性、可見性、有序性的保證。

3、詳解-jmm保證

主要介紹下jmm如何保證原子性、可見性,有序性。

jmm保證對除long和double外的基礎資料型別的讀寫操作是原子性的。另外關鍵字Synchronized也可以提供原子性保證。Synchronized的原子性是通過java的兩個高階的位元組碼指令monitorenter和monitorexit來保證的。

jmm可見性的保證,一個是通過Synchronized,另外一個就是volatile。volatile強制變數的賦值會同步重新整理回主記憶體,強制變數的讀取會從主記憶體重新載入,保證不同的執行緒總是能夠看到該變數的最新值。

jmm對有序性的保證,主要通過volatile和一系列happens-before原則。volatile的另一個作用就是阻止指令重排序,這樣就可以保證變數讀寫的有序性。

happens-before原則包括一系列規則,如

  • 程式順序原則,即一個執行緒內必須保證語義序列性;

  • 鎖規則,即對同一個鎖的解鎖一定發生在再次加鎖之前;

  • 此外還包括happens-before原則的傳遞性、執行緒啟動、中斷、終止規則等。

二、類載入機制相關考點

1.詳解類載入機制

類的載入指的是將編譯好的class類檔案中的位元組碼讀入到記憶體中,將其放在方法區內並建立對應的Class物件。

類的載入分為載入、連結、初始化,其中連結又包括驗證、準備、解析三步。看到圖中上半部分深綠色,我們逐個分析:

  • 載入是檔案到記憶體的過程。通過類的完全限定名查詢此類位元組碼檔案,並利用位元組碼檔案建立一個Class物件

  • 驗證是對類檔案內容驗證。目的在於確保Class檔案符合當前虛擬機器要求,不會危害虛擬機器自身安全。主要包括四種:檔案格式驗證,後設資料驗證,位元組碼驗證,符號引用驗證。

  • 準備階段是進行記憶體分配。為類變數也就是類中由static修飾的變數分配記憶體,並且設定初始值,這裡要注意,初始值是0或者null,而不是程式碼中設定的具體值,程式碼中設定的值是在初始化階段完成的。另外這裡也不包含用final修飾的靜態變數,因為final在編譯的時候就會分配了。

  • 解析主要是解析欄位、介面、方法。主要是將常量池中的符號引用替換為直接引用的過程。直接引用就是直接指向目標的指標、相對偏移量等。

  • 最後是初始化:主要完成靜態塊執行與靜態變數的賦值。這是類載入最後階段,若被載入類的父類沒有初始化,則先對父類進行初始化。

只有對類主動使用時,才會進行初始化,初始化的觸發條件包括建立類的例項的時候、訪問類的靜態方法或者靜態變數的時候、Class.forName()反射類的時候、或者某個子類被初始化的時候。

類的生命週期,就是從類的載入到類例項的建立與使用,再到類物件不再被使用時可以被GC解除安裝回收。這裡要注意一點,由java虛擬機器自帶的三種類載入器載入的類在虛擬機器的整個生命週期中是不會被解除安裝的,只有使用者自定義的類載入器所載入的類才可以被解除安裝。

2.詳解類載入器

java自帶的三種類載入器分別是:bootstrap啟動類載入器、擴充套件類載入器和應用載入器也叫系統載入器。圖右邊的桔黃色文字表示各類載入器對應的載入目錄。啟動類載入器載入java home中lib目錄下的類,擴充套件載入器負責載入ext目錄下的類,應用載入器載入classpath指定目錄下的類。

除此之外,可以自定義類載入器。

java的類載入使用雙親委派模式,即一個類載入器在載入類時,先把這個請求委託給自己的父類載入器去執行,如果父類載入器還存在父類載入器,就繼續向上委託,直到頂層的啟動類載入器,如圖中藍色向上的箭頭。如果父類載入器能夠完成類載入,就成功返回,如果父類載入器無法完成載入,那麼子載入器才會嘗試自己去載入。

這種雙親委派模式的好處,一個可以避免類的重複載入,另外也避免了java的核心API被篡改。

三、其他知識梳理

1.詳解分代回收

前面提到過,java的堆記憶體被分代管理,分代管理主要是為了方便垃圾回收,這樣做基於2個事實,第一、大部分物件很快就不再使用,第二,還有一部分不會立即無用,但也不會持續很長時間。

虛擬機器中劃分為年輕代、老年代、和永久代。

1>年輕代:主要用來存放新建立的物件,年輕代分為eden區和兩個Survivor區。大部分物件在Eden區中生成。當Eden區滿時,還存活的物件會在兩個Survivor區交替儲存,達到一定次數的物件會晉升到老年代。

2>老年代:用來存放從年輕代晉升而來的,存活時間較長的物件。

3>永久代:主要儲存類資訊等內容,這裡的永久代是指物件劃分方式,不是專指1.7的permGen,或者1.8之後的metaspace。

根據年輕代與老年代的特點,jvm提供了不同的垃圾回收演算法。垃圾回收演算法按型別可以分為引用計數法、複製法和標記清除法。

其中引用計數法是通過物件被引用的次數來確定物件是否被使用,缺點是無法解決迴圈引用的問題。

複製演算法需要from和to兩塊相同大小的記憶體空間,物件分配時只在from塊中進行,回收時把存活物件複製到to塊中,並清空from塊,然後交換兩塊的分工,即把from塊作為to塊,把to塊作為from塊。缺點是記憶體使用率較低。

標記清除演算法分為標記物件和清除不在使用的物件兩個階段,標記清除演算法的缺點是會產生記憶體碎片。

jvm中提供的年輕代回收演算法Serial、ParNew、Parallel Scavenge都是複製演算法,而CMS、G1、zgc都屬於標記清除演算法。

本篇文章,對這幾個演算法就不展開了

 

總結:面試考察點及加分項

1.jvm相關的面試考察點

首先,需要jvm的記憶體模型和java的記憶體模型;

其次,要了解的類的載入過程,瞭解雙親委派機制;

第三,要理解記憶體的可見性與java記憶體模型對原子性、可見性、有序性的保證機制;

第四,要了解常用的gc演算法的特點、執行過程,和適用場景,例如g1適合對最大延遲有要求的場合,zgc適用於64為系統的大記憶體服務中;

第五,要了解常用的jvm引數,明白對不同引數的調整會有怎樣的影響,適用什麼樣的場景。例如垃圾回收的併發數、偏向鎖設定等

2.相關加分項

如果想要面試官對你留下更好的印象的話,注意這些加分項:

  • 首先,如果在編譯器優化方面有深入的瞭解的話,會讓面試官覺得你對技術的深度比較有追求。例如知道在程式設計時如何合理利用棧上分配降低gc壓力、如何編寫適合內聯優化等程式碼等。

  • 其次,如果你能有線上實際問題的排查經驗或思路那就更好了,面試官都喜歡動手能力強的同學。例如解決過線上經常full gc問題,排查過記憶體洩露問題等。

  • 第三,如果能有針對特定場景的jvm優化實踐或者優化思路,也會有意想不到的效果。例如針對高併發低延遲的場景,如何調整gc引數儘量降低gc停頓時間,針對佇列處理機如何儘可能提高吞吐率等;

  • 第四,如果對最新的jvm技術趨勢有所瞭解,也會給面試官留下比較深刻的印象。例如瞭解zgc高效的實現原理,瞭解Graalvm的特點等。

總之,掌握以上具體的JVM考點,才能在面試時應答自如。希望讀完此篇文章的你,能做好準備,拿到心儀的Offer。

相關文章