還沒搞懂JVM嗎?95%的技術(shù)面試必問知識(shí)點(diǎn)都在這,還怕面不過?

概述:知識(shí)點(diǎn)匯總

jvm的知識(shí)點(diǎn)匯總共6個(gè)大方向:內(nèi)存模型、類加載機(jī)制、GC垃圾回收是比較重點(diǎn)的內(nèi)容。性能調(diào)優(yōu)部分偏重實(shí)際應(yīng)用,重點(diǎn)突出實(shí)踐能力。編譯器優(yōu)化和執(zhí)行模式部分偏重理論基礎(chǔ),主要掌握知識(shí)點(diǎn)。

各個(gè)部分的內(nèi)容如下:

1>內(nèi)存模型部分:程序計(jì)數(shù)器、方法區(qū)、堆、棧、本地方法棧的作用,保存哪些數(shù)據(jù);

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

3>GC部分:分代回收的思想和依據(jù),以及不同垃圾回收算法實(shí)現(xiàn)的思路、適合的場景;

4>性能調(diào)優(yōu)部分:常用的jvm優(yōu)化參數(shù)的作用,參數(shù)調(diào)優(yōu)的依據(jù),要了解常用的jvm分析工具能分析哪類問題,以及使用方法;

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

6>編譯優(yōu)化部分:前端編譯器javac的編譯過程、AST抽象語法樹、編譯期優(yōu)化和運(yùn)行期優(yōu)化。編譯優(yōu)化的常用技術(shù),包括公共子表達(dá)式的消除、方法內(nèi)聯(lián)、逃逸分析、棧上分配、同步消除等。明白了這些才能寫出對編譯器友好的代碼。

jvm的內(nèi)容相對來說比較集中,但是對知識(shí)深度的掌握要求較高,建議面試前重點(diǎn)加強(qiáng)。

一、jvm內(nèi)存相關(guān)考點(diǎn)

1.詳解-jvm內(nèi)存模型

jvm內(nèi)存模型主要指運(yùn)行時(shí)的數(shù)據(jù)區(qū),包括5個(gè)部分。

棧也叫方法棧,是線程私有的,線程在執(zhí)行每個(gè)方法時(shí)都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀,用來存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等信息。調(diào)用方法時(shí)執(zhí)行入棧,方法返回時(shí)執(zhí)行出棧。

本地方法棧與棧類似,也是用來保存線程執(zhí)行方法時(shí)的信息,不同的是,執(zhí)行java方法使用棧,而執(zhí)行native方法使用本地方法棧。

程序計(jì)數(shù)器保存著當(dāng)前線程所執(zhí)行的字節(jié)碼位置,每個(gè)線程工作時(shí)都有一個(gè)獨(dú)立的計(jì)數(shù)器。程序計(jì)數(shù)器為執(zhí)行java方法服務(wù),執(zhí)行native方法時(shí),程序計(jì)數(shù)器為空。

棧、本地方法棧、程序計(jì)數(shù)器這三個(gè)部分都是線程獨(dú)占的。

堆是jvm管理的內(nèi)存中最大的一塊,堆被所有線程共享,目的是為了存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配。當(dāng)堆內(nèi)存沒有可用的空間時(shí),會(huì)拋出OOM異常。根據(jù)對象存活的周期不同,jvm把堆內(nèi)存進(jìn)行分代管理,由垃圾回收器來進(jìn)行對象的回收管理。

方法區(qū)也是各個(gè)線程共享的內(nèi)存區(qū)域,又叫非堆區(qū)。用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),

jdk1.7中的永久代和1.8中的metaspace都是方法區(qū)的一種實(shí)現(xiàn)。

面試回答此知識(shí)點(diǎn)相關(guān)問題時(shí),要答出兩個(gè)要點(diǎn):一個(gè)是各部分的功能,另一個(gè)是哪些線程共享,哪些獨(dú)占。

2.詳解-jmm內(nèi)存可見性

jmm是java內(nèi)存模型,與剛才講到的jvm內(nèi)存模型是兩回事,jmm的主要目標(biāo)是定義程序中變量的訪問規(guī)則,如圖所示,所有的共享變量都存儲(chǔ)在主內(nèi)存中共享。每個(gè)線程有自己的工作內(nèi)存,工作內(nèi)存中保存的是主內(nèi)存中變量的副本,線程對變量的讀寫等操作必須在自己 的工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的變量。

在多線程進(jìn)行數(shù)據(jù)交互時(shí),例如線程a給一個(gè)共享變量賦值后,由線程b來讀取這個(gè)值,a修改完變量是修改在自己的工作區(qū)內(nèi)存中,b是不可見的,只有從a的工作區(qū)寫回到主內(nèi)存,b再從主內(nèi)存讀取到自己的工作區(qū)才能進(jìn)行進(jìn)一步的操作。由于指令重排序的存在,這個(gè)寫-讀的順序有可能被打亂。

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

3、詳解-jmm保證

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

jmm保證對除long和double外的基礎(chǔ)數(shù)據(jù)類型的讀寫操作是原子性的。另外關(guān)鍵字Synchronized也可以提供原子性保證。Synchronized的原子性是通過java的兩個(gè)高級(jí)的字節(jié)碼指令monitorenter和monitorexit來保證的。

jmm可見性的保證,一個(gè)是通過Synchronized,另外一個(gè)就是volatile。volatile強(qiáng)制變量的賦值會(huì)同步刷新回主內(nèi)存,強(qiáng)制變量的讀取會(huì)從主內(nèi)存重新加載,保證不同的線程總是能夠看到該變量的最新值。

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

  • happens-before原則包括一系列規(guī)則,如
  • 程序順序原則,即一個(gè)線程內(nèi)必須保證語義串行性;
  • 鎖規(guī)則,即對同一個(gè)鎖的解鎖一定發(fā)生在再次加鎖之前;
  • 此外還包括happens-before原則的傳遞性、線程啟動(dòng)、中斷、終止規(guī)則等。

二、類加載機(jī)制相關(guān)考點(diǎn)

1.詳解類加載機(jī)制

類的加載指的是將編譯好的class類文件中的字節(jié)碼讀入到內(nèi)存中,將其放在方法區(qū)內(nèi)并創(chuàng)建對應(yīng)的Class對象。

類的加載分為加載、鏈接、初始化,其中鏈接又包括驗(yàn)證、準(zhǔn)備、解析三步??吹綀D中上半部分深綠色,我們逐個(gè)分析:

加載是文件到內(nèi)存的過程。通過類的完全限定名查找此類字節(jié)碼文件,并利用字節(jié)碼文件創(chuàng)建一個(gè)Class對象

驗(yàn)證是對類文件內(nèi)容驗(yàn)證。目的在于確保Class文件符合當(dāng)前虛擬機(jī)要求,不會(huì)危害虛擬機(jī)自身安全。主要包括四種:文件格式驗(yàn)證,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證。

準(zhǔn)備階段是進(jìn)行內(nèi)存分配。為類變量也就是類中由static修飾的變量分配內(nèi)存,并且設(shè)置初始值,這里要注意,初始值是0或者null,而不是代碼中設(shè)置的具體值,代碼中設(shè)置的值是在初始化階段完成的。另外這里也不包含用final修飾的靜態(tài)變量,因?yàn)閒inal在編譯的時(shí)候就會(huì)分配了。

解析主要是解析字段、接口、方法。主要是將常量池中的符號(hào)引用替換為直接引用的過程。直接引用就是直接指向目標(biāo)的指針、相對偏移量等。

最后是初始化:主要完成靜態(tài)塊執(zhí)行與靜態(tài)變量的賦值。這是類加載最后階段,若被加載類的父類沒有初始化,則先對父類進(jìn)行初始化。

只有對類主動(dòng)使用時(shí),才會(huì)進(jìn)行初始化,初始化的觸發(fā)條件包括創(chuàng)建類的實(shí)例的時(shí)候、訪問類的靜態(tài)方法或者靜態(tài)變量的時(shí)候、Class.forName()反射類的時(shí)候、或者某個(gè)子類被初始化的時(shí)候。

類的生命周期,就是從類的加載到類實(shí)例的創(chuàng)建與使用,再到類對象不再被使用時(shí)可以被GC卸載回收。這里要注意一點(diǎn),由java虛擬機(jī)自帶的三種類加載器加載的類在虛擬機(jī)的整個(gè)生命周期中是不會(huì)被卸載的,只有用戶自定義的類加載器所加載的類才可以被卸載。

2.詳解類加載器

java自帶的三種類加載器分別是:bootstrap啟動(dòng)類加載器、擴(kuò)展類加載器和應(yīng)用加載器也叫系統(tǒng)加載器。圖右邊的桔黃色文字表示各類加載器對應(yīng)的加載目錄。啟動(dòng)類加載器加載java home中l(wèi)ib目錄下的類,擴(kuò)展加載器負(fù)責(zé)加載ext目錄下的類,應(yīng)用加載器加載classpath指定目錄下的類。

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

java的類加載使用雙親委派模式,即一個(gè)類加載器在加載類時(shí),先把這個(gè)請求委托給自己的父類加載器去執(zhí)行,如果父類加載器還存在父類加載器,就繼續(xù)向上委托,直到頂層的啟動(dòng)類加載器,如圖中藍(lán)色向上的箭頭。如果父類加載器能夠完成類加載,就成功返回,如果父類加載器無法完成加載,那么子加載器才會(huì)嘗試自己去加載。

這種雙親委派模式的好處,一個(gè)可以避免類的重復(fù)加載,另外也避免了java的核心API被篡改。

三、其他知識(shí)梳理

1.詳解分代回收

前面提到過,java的堆內(nèi)存被分代管理,分代管理主要是為了方便垃圾回收,這樣做基于2個(gè)事實(shí),第一、大部分對象很快就不再使用,第二,還有一部分不會(huì)立即無用,但也不會(huì)持續(xù)很長時(shí)間。

虛擬機(jī)中劃分為年輕代、老年代、和永久代。

1>年輕代:主要用來存放新創(chuàng)建的對象,年輕代分為eden區(qū)和兩個(gè)Survivor區(qū)。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí),還存活的對象會(huì)在兩個(gè)Survivor區(qū)交替保存,達(dá)到一定次數(shù)的對象會(huì)晉升到老年代。

2>老年代:用來存放從年輕代晉升而來的,存活時(shí)間較長的對象。

3>永久代:主要保存類信息等內(nèi)容,這里的永久代是指對象劃分方式,不是專指1.7的permGen,或者1.8之后的metaspace。

根據(jù)年輕代與老年代的特點(diǎn),jvm提供了不同的垃圾回收算法。垃圾回收算法按類型可以分為引用計(jì)數(shù)法、復(fù)制法和標(biāo)記清除法。

其中引用計(jì)數(shù)法是通過對象被引用的次數(shù)來確定對象是否被使用,缺點(diǎn)是無法解決循環(huán)引用的問題。

復(fù)制算法需要from和to兩塊相同大小的內(nèi)存空間,對象分配時(shí)只在from塊中進(jìn)行,回收時(shí)把存活對象復(fù)制到to塊中,并清空from塊,然后交換兩塊的分工,即把from塊作為to塊,把to塊作為from塊。缺點(diǎn)是內(nèi)存使用率較低。

標(biāo)記清除算法分為標(biāo)記對象和清除不在使用的對象兩個(gè)階段,標(biāo)記清除算法的缺點(diǎn)是會(huì)產(chǎn)生內(nèi)存碎片。

jvm中提供的年輕代回收算法Serial、ParNew、Parallel Scavenge都是復(fù)制算法,而CMS、G1、zgc都屬于標(biāo)記清除算法。

四、jvm面試題分享

  1. 說一下 jvm 的主要組成部分?及其作用?
  2. 說一下 jvm 運(yùn)行時(shí)數(shù)據(jù)區(qū)?
  3. 說一下堆棧的區(qū)別?
  4. 隊(duì)列和棧是什么?有什么區(qū)別?
  5. 什么是雙親委派模型?
  6. 說一下類加載的執(zhí)行過程?
  7. 怎么判斷對象是否可以被回收?
  8. java 中都有哪些引用類型?
  9. 說一下 jvm 有哪些垃圾回收算法?
  10. 說一下 jvm 有哪些垃圾回收器?
  11. 詳細(xì)介紹一下 CMS 垃圾回收器?
  12. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么區(qū)別?
  13. 簡述分代垃圾回收器是怎么工作的?
  14. 說一下 jvm 調(diào)優(yōu)的工具?
  15. 常用的 jvm 調(diào)優(yōu)的參數(shù)都有哪些?
  16. Java的內(nèi)存模型以及GC算法
  17. jvm性能調(diào)優(yōu)都做了什么
  18. 介紹JVM中7個(gè)區(qū)域,然后把每個(gè)區(qū)域可能造成內(nèi)存的溢出的情況說明。
  19. 介紹GC 和GC Root不正常引用
  20. 自己從classload 加載方式,加載機(jī)制說開去,從程序運(yùn)行時(shí)數(shù)據(jù)區(qū),講到內(nèi)存分配,講到String常量池,講到JVM垃圾回收機(jī)制,算法,hotspot。
  21. jvm 如何分配直接內(nèi)存, new 對象如何不分配在堆而是棧上,常量池解析。
  22. 數(shù)組多大放在JVM老年代
  23. 老年代中數(shù)組的訪問方式
  24. GC 算法,永久代對象如何 GC , GC 有環(huán)怎么處理。
  25. 誰會(huì)被 GC ,什么時(shí)候 GC。
  26. 如果想不被 GC 怎么辦
  27. 如果想在 GC 中生存 1 次怎么辦

總結(jié):面試考察點(diǎn)及加分項(xiàng)

1.jvm相關(guān)的面試考察點(diǎn)

首先,需要jvm的內(nèi)存模型和java的內(nèi)存模型;
其次,要了解的類的加載過程,了解雙親委派機(jī)制;
第三,要理解內(nèi)存的可見性與java內(nèi)存模型對原子性、可見性、有序性的保證機(jī)制;
第四,要了解常用的gc算法的特點(diǎn)、執(zhí)行過程,和適用場景,例如g1適合對最大延遲有要求的場合,zgc適> 用于64為系統(tǒng)的大內(nèi)存服務(wù)中;
第五,要了解常用的jvm參數(shù),明白對不同參數(shù)的調(diào)整會(huì)有怎樣的影響,適用什么樣的場景。例如垃圾回收的并發(fā)數(shù)、偏向鎖設(shè)置等

2.相關(guān)加分項(xiàng)

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

首先,如果在編譯器優(yōu)化方面有深入的了解的話,會(huì)讓面試官覺得你對技術(shù)的深度比較有追求。例如知道在編程時(shí)如何合理利用棧上分配降低gc壓力、如何編寫適合內(nèi)聯(lián)優(yōu)化等代碼等。

其次,如果你能有線上實(shí)際問題的排查經(jīng)驗(yàn)或思路那就更好了,面試官都喜歡動(dòng)手能力強(qiáng)的同學(xué)。例如解決過線上經(jīng)常full gc問題,排查過內(nèi)存泄露問題等。

第三,如果能有針對特定場景的jvm優(yōu)化實(shí)踐或者優(yōu)化思路,也會(huì)有意想不到的效果。例如針對高并發(fā)低延遲的場景,如何調(diào)整gc參數(shù)盡量降低gc停頓時(shí)間,針對隊(duì)列處理機(jī)如何盡可能提高吞吐率等;

第四,如果對最新的jvm技術(shù)趨勢有所了解,也會(huì)給面試官留下比較深刻的印象。例如了解zgc高效的實(shí)現(xiàn)原理,了解Graalvm的特點(diǎn)等。

總之,掌握以上具體的JVM考點(diǎn),才能在面試時(shí)應(yīng)答自如。希望讀完此篇文章的你,都能在來年金三銀四的招聘季做好準(zhǔn)備,拿到心儀的Offer。

最后針對于上面的文章我總結(jié)出了互聯(lián)網(wǎng)公司java程序員面試涉及到的絕大部分面試題及答案做成了文檔和架構(gòu)視頻資料免費(fèi)分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發(fā)等架構(gòu)技術(shù)資料),希望能幫助到您面試前的復(fù)習(xí)且找到一個(gè)好的工作,也節(jié)省大家在網(wǎng)上搜索資料的時(shí)間來學(xué)習(xí)。

領(lǐng)取方式:點(diǎn)贊關(guān)注小編后進(jìn)我的架構(gòu)交流學(xué)習(xí)群:909666042 免費(fèi)獲??!

合理利用自己每一分每一秒的時(shí)間來學(xué)習(xí)提升自己,不要再用"沒有時(shí)間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個(gè)交代!

更多筆記分享

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 介紹JVM中7個(gè)區(qū)域,然后把每個(gè)區(qū)域可能造成內(nèi)存的溢出的情況說明 程序計(jì)數(shù)器:看做當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)指示器...
    jemmm閱讀 2,308評論 0 9
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,713評論 1 4
  • 工作之余,想總結(jié)一下JVM相關(guān)知識(shí)。 Java運(yùn)行時(shí)數(shù)據(jù)區(qū): Java虛擬機(jī)在執(zhí)行Java程序的過程中會(huì)將其管理的...
    Huang遠(yuǎn)閱讀 687評論 0 2
  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,298評論 0 2
  • 《深入理解Java虛擬機(jī)》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,481評論 1 34

友情鏈接更多精彩內(nèi)容