95%的技術面試必考的JVM知識點都在這,另附加分思路!
概述:知識點匯總
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都屬於標記清除算法。
本篇文章,對這幾個算法就不展開了,具體可見《32個Java面試必考點》
總結:面試考察點及加分項
1.jvm相關的面試考察點
首先,需要jvm的內存模型和java的內存模型;
其次,要了解的類的加載過程,了解雙親委派機制;
第三,要理解內存的可見性與java內存模型對原子性、可見性、有序性的保證機制;
第四,要了解常用的gc算法的特點、執行過程,和適用場景,例如g1適合對最大延遲有要求的場合,zgc適用於64為系統的大內存服務中;
第五,要了解常用的jvm參數,明白對不同參數的調整會有怎樣的影響,適用什麽樣的場景。例如垃圾回收的並發數、偏向鎖設置等
2.相關加分項
如果想要面試官對你留下更好的印象的話,註意這些加分項:
首先,如果在編譯器優化方面有深入的了解的話,會讓面試官覺得你對技術的深度比較有追求。例如知道在編程時如何合理利用棧上分配降低gc壓力、如何編寫適合內聯優化等代碼等。
其次,如果你能有線上實際問題的排查經驗或思路那就更好了,面試官都喜歡動手能力強的同學。例如解決過線上經常full gc問題,排查過內存泄露問題等。
第三,如果能有針對特定場景的jvm優化實踐或者優化思路,也會有意想不到的效果。例如針對高並發低延遲的場景,如何調整gc參數盡量降低gc停頓時間,針對隊列處理機如何盡可能提高吞吐率等;
第四,如果對最新的jvm技術趨勢有所了解,也會給面試官留下比較深刻的印象。例如了解zgc高效的實現原理,了解Graalvm的特點等。
總之,掌握以上具體的JVM考點,才能在面試時應答自如。希望讀完此篇文章的你,都能在金三銀四的招聘季做好準備,拿到心儀的Offer。
以上內容摘取自《32個Java面試必考點》第03講:深入淺出JVM
本課程結合了拉勾40W+技術崗位大數據,對資深工程師的入職要求進行提煉,除了本篇文章的提到的JVM相關內容,也幫你梳理年薪50萬Java工程師必會知識點框架,讓你用200分鐘,補齊工作能力短板,還能學會如何更好的面向上級、團隊展現自己的優勢。
用於面試前的查漏補缺、職業規劃和晉升評審,都有一定參考價值。
升職季限時活動:即日起至3月31日,購買本課程後,憑入職offer可申請全額退款!限量100名。具體可見《32個Java面試必考點》
95%的技術面試必考的JVM知識點都在這,另附加分思路!