佔小狼 轉載請註明原創出處,謝謝!
願你被這個世界溫柔以待
從《關於Java面試,你應該準備這些知識點》 一文的閱讀量和點贊程度可以發現,貌似大家更喜歡這類文章,也許是技術型的文章看著比較的枯燥,這些只是我近段時間求職面試時所遇到的一些問題,整理出來希望對有需要的同學提供幫助,可以更系統的去學習各個知識點。
####虛擬機器JVM相關 這塊內容並非每個面試官都會問,但是如果是應聘高階職位的話,這一環節是不可缺少的,面試的難易程度也不一樣,有些面試官或許讓你講講虛擬機器的記憶體模型即可,有些也會讓你解釋垃圾回收的實現,當然也會有虛擬機器調優的實戰經驗,線上問題排查等等。
場景對話: 面試官:Java虛擬機器有了解麼?
我:恩,略有接觸過...(水哥說過,話不能說太滿,容易打臉)
面試官:那你先講講它的記憶體模型吧
我:Java堆,Java棧,程式計數器,方法區,1.7的永久代,1.8的metaspace....(噼裡啪啦概念講一通,簡短描述下每個記憶體區的用途,能想到的都講出來,不要保留,不要等面試官問 “還有嗎?”)
面試官:好,一般Java堆是如何實現的?
我:在HotSpot虛擬機器實現中,Java堆分成了新生代和老年代,我當時看的是1.7的實現,所有還有永久代,新生代中又分為了eden區和survivor區,survivor區又分成了S0和S1,或則是from和to,(這個時候,我要求紙和筆,因為我覺得這個話題可以聊蠻長時間,又是我比較熟悉的...一邊畫圖,一邊描述),其中eden,from和to的記憶體大小預設是8:1:1(各種細節都要說出來...),此時,我已經在紙上畫出了新生代和老年代代表的區域
面試官:恩,給我講講物件在記憶體中的初始化過程?
我:(千萬不要只說,新物件在Java堆進行記憶體分配並初始化,或是在eden區進行記憶體分配並初始化)要初始化一個物件,首先要載入該物件所對應的class檔案,該檔案的資料會被載入到永久代,並建立一個底層的instanceKlass物件代表該class,再為將要初始化的物件分配記憶體空間,優先線上程私有記憶體空間中分配大小,如果空間不足,再到eden中進行記憶體分配...^&&*%
面試官:恩,好,說下YGC的大概過程...
我:先找出根物件,如Java棧中引用的物件、靜態變數引用的物件和系統詞典中引用的物件等待,把這些物件標記成活躍物件,並複製到to區,接著遍歷這些活躍物件中引用的物件並標記,找出老年代物件在eden區有引用關係的物件並標記,最後把這些標記的物件複製到to,在複製過程還要判斷活躍物件的gc年齡是否已經達到閾值,如果已經達到閾值,就直接晉升到老年代,YGC結束之後把from和to的引用互換(能多說點就多說點,省的面試官再提問,我把老年代的cms回收也大致說了一遍,以為面試官會跳過這個話題了,還是太年輕了)。
面試官:你剛剛說到在YGC的時候,有些物件可能會發生晉升,如果晉升失敗怎麼處理?
我:....(斷片了幾秒鐘,我記得我分析過這段程式碼的,但是印象不深刻了)我記得在標記階段時,會把物件和對應的物件頭資料儲存在兩個棧中,如果晉升失敗的話,就把該物件的物件頭復原...
面試官:那你在實際專案中有碰到這種情況麼,會導致什麼問題?
我:...(這我真沒有遇到過)對,有遇到過一次,在分析gc日誌的時候,發現YGC發生之後,日誌顯示gc後的記憶體變大了,後來查出來是因為物件的晉升失敗造成的。(我隱約記得看過笨神的一篇文章,回答的心裡很虛)
面試官:(沒有反駁,繼續問)有過虛擬機器效能調優的經驗麼?
我:(說實話,調優經驗真的不多)恩,有一點吧,不是很足,就是我們XX專案上線的時候,發現YGC特別的頻繁^^&^8&,通過調整新生代的大小(線上環境的虛擬機器引數是預設的),同時檢查業務邏輯程式碼&*&$$~~!
面試官:恩?還有麼?
我:(面試這麼久,好怕面試官的下一句是 “恩?還有麼?”,顯然面試官還不滿足我的回答,但是我也只能答到這個地步了...)恩,經驗確實有限,目前就根據這個專案做過一些相關的優化。
面試官: 。。。。。。
我:。。。。。。
面試官: 那我們看看別的吧。
關於虛擬機器方面的文章,我針對hotSpot的實現寫了一些分析,感興趣的同學可以看看,這些文章看著確實有點枯燥。
相關文章: JVM原始碼分析之JVM啟動流程 JVM原始碼分析之堆記憶體的初始化 JVM原始碼分析之Java類的載入過程 JVM原始碼分析之Java物件的建立過程 JVM原始碼分析之如何觸發並執行GC執行緒 JVM原始碼分析之垃圾收集的執行過程 JVM原始碼分析之新生代DefNewGeneration的實現 JVM原始碼分析之老年代TenuredGeneration的垃圾回收演算法實現
###細節問題 細節決定成敗,在面試過程中,雖然也有運氣的成分存在,但是對於細節的掌握程度,可以很好的衡量應試者的技術水平。
####volatile 場景對話: 面試官:說說volatile關鍵字的實現原理
我:volatile關鍵字提供了記憶體可見性和禁止記憶體重排序
面試官:分別解釋一下
我:因為在虛擬機器記憶體中有主記憶體和工作記憶體的概念,每個cpu都有自己的工作記憶體,當讀取一個普通變數時,優先讀取工作記憶體的變數,如果工作記憶體中沒有對應的變數,則從主記憶體中載入到工作記憶體,對工作記憶體的普通變數進行修改,不會立馬同步到主記憶體,記憶體可見性保證了在多執行緒的場景下,保證了執行緒A對變數的修改,其它執行緒可以讀到最新值&&%%……
面試官:如何保證的?
我:當對volatile修飾的變數進行寫操作時,直接把最新值寫到主記憶體中,並清空其它cpu工作記憶體中該變數所在的記憶體行資料,當對volatile修飾的變數進行讀操作時,會讀取主記憶體的資料&&&%%¥@
面試官:你知道系統級別是如何實現的麼?
我:(what,what are u 說啥呢)我記得操作volatile變數的彙編程式碼前面會有lock字首指令
面試官:你這說的還是程式碼層面,我說的是系統級別
我:(懵逼臉...)這個再底層下去我真的沒研究過了...
相關文章:《java volatile關鍵字解惑》
####Object.finalize 場景對話: 面試官:和我講講Object類的finalize方法的實現原理
我:(完全沒想到面試官會問這個)新建一個物件時,在JVM中會判斷該物件對應的類是否重寫了finalize方法,且finalize方法體不為空,則把該物件封裝成Finalizer物件,並新增到Finalizer連結串列。
面試官:恩,然後呢?
我:Finalizer類中會初始化一個FinalizerThread型別的執行緒,負責從一個引用佇列中獲取Finalizer物件,並執行該Finalizer物件的runFinalizer方法,最終會執行原始物件的finalize方法,&&%%##(這塊邏輯有點繞,當時答的也有點虛)
面試官:Finalizer物件什麼時候會在引用佇列中?
我:(努力回想中)在發生GC的時候,具體在什麼時間點或如何被插入到引用佇列中,這塊實現我已經忘記了...(我真的忘記了,只記得這塊邏輯太複雜了)
面試官:恩,你驗證過finalize方法是否會執行麼?
我:恩,自己寫過例子證明過,也看過原始碼的實現。
面試官:怎麼證明的?
我:初始化一個大陣列,可以明顯看出gc之後是否被回收,然後執行System.gc(),在finalize方法中輸出資訊 &&%%@@,(把之前做過的驗證說一遍)
面試官:恩,可以...
相關文章: 《深入分析Object.finalize方法的實現原理》
###大問題 什麼是大問題,就是問題很大,讓你自己去理解,把你的畢生所學都拿出來.
場景對話: 面試官:如果給你一個系統,如何去優化?
我:(優化什麼?效能,穩定性,還是其它方面,只能硬著頭皮上了,結合自己做的一個專案) 1、分析系統,定義指標 2、通過系統埋點,收集指標的度量值,對指標進行迭代優化&&^%&$#
面試官:就這些?沒了麼?
我:(因為是電話面試,感覺當時腦袋是空白的,估計和麵試官的級別也有關係)如果指標是介面效能的話,可以看下系統記憶體是不是可以使用快取進行效能上的優化,比如redis,如果是訪問很頻繁又不會經常變動的資料,如熱點資料,可以直接使用本地快取進行優化,畢竟一次網路請求也需要1~2毫秒
面試官:沒了麼?
我:(因為自己系統優化的經驗確實不豐富,讓面試官覺得怎麼就只能想到如此少的優化點呢)資料庫的讀寫分離,資料庫的分庫分表,如果經常條件查詢資料庫的話,可以引入搜尋服務es或則lucene進行優化