java架構之路-(面試篇)JVM虛擬機面試大全

  • 2019 年 10 月 9 日
  • 筆記

  下文連接比較多啊,都是我過整理的博客,很多答案都在博客里有詳細說明,理解記憶是最紮實的記憶。而且我的答案不一定是最準確的,但是我的答案不會讓你失望,而且幾乎每個答案都是問題的擴展答案。

1.JVM內存模型

  答:https://www.cnblogs.com/cxiaocai/p/11483629.html  

先經過類裝載子系統裝載,經由驗證,準備,解析,初始化四個過程。方法被調用,方法進棧,對象放在堆,棧內存的引用指向我們的對象,方法逐行執行,由我們的程序計數器來控制。元空間放置靜態方法,不佔用jvm內存,佔用的是實際內存空間。 

2.JVM垃圾回收算法

  答:三種算法。https://www.cnblogs.com/cxiaocai/p/11547743.html

  複製算法,由一個內存塊複製到一個內存塊下,缺點是有一半的內存是留給複製用的,不用用於直接存儲

  標記清理,掃逐個內存塊是否可被回收,可以回收的就回收掉,缺點是會產生內存碎片

  標記整理,標記整理是是標記清除的升級版,優點:解決內存碎片問題。缺點:整理階段,由於移動了可用對象,需要去更新引用。

3.JVM垃圾收集器有哪些?以及優劣勢比較?

  答:https://www.cnblogs.com/cxiaocai/p/11547743.html

  有五種垃圾回收器,分別是串行的Serial,並行的ParNew,高CPU的Parallel Scavenge,還有我們的CMS和大內存的G1。

  需要說的是CMS回收器的過程,初始標記(STW),並發標記,重新標記(STW,耗時70%),並發清理 ,並發重置。

  G1回收器適合大內存的服務器,回收過程是,初始標記(STW),並發標記,最終標記(STW),篩選回收(STW)。

  G1回收器的優點是STW時間是可以控制的。串行的Serial,自我覺得沒啥優點,唯一的優點就是清理的徹底吧。並行的ParNew,開啟多個線程去回收,一般來說線程數和CPU的核數相等,但不建議用在老年代,在老年代兼容性差。高CPU的Parallel Scavenge可能會搶佔用戶線程的CPU。CMS回收器需要考慮到老年代擔保機制。

  https://www.cnblogs.com/cxiaocai/p/11570519.html

  再就是G1三個GC都是什麼YoungGC優先判斷一下回收一次的工作效率,不值得不執行。MixedGC清理內存塊,複製算法,Full GC停止系統程序,然後採用單線程進行標記、清理和壓縮整理,好空閑出來一批Region來供下一次MixedGC使用

4.什麼是類的加載

  答案:https://www.cnblogs.com/cxiaocai/p/11483629.html 

    1.驗證:驗證我們的編譯文件(位元組碼文件)是否正確。

    2.準備:給予類的靜態常量開闢堆空間。並且賦予默認值。對象也在這個時候放置在堆空間,並且給予空值。

    3.解析:將符號引用替換為直接引用,該階段會把一些靜態方法(符號引用,比如main()方法)替換為指向數據所存內存的指針或句柄等(直接引用),這是所謂的靜態鏈接過程(類加載期間完成),動態鏈接是在程序運行期間完成的將符號引用替換為直接引用。就像是我們把main轉化為001,將()轉化為002,將這一系列的編碼存在堆上。

    4.初始化,將第3步的靜態常量(或對象)賦值,執行靜態代碼塊。

5.簡述雙親委派

  答:https://www.cnblogs.com/cxiaocai/p/11483629.html

  這個簡單啦,別管幾層,下面給我的我一律不管,除非我是最頂層,上級交給我的,我看看我可以做嗎?做不了我給我下級。

  用技術一點的話就是類來了,交給我們的自定義加載器,自定義加載器先不做處理,交給我們的應用類加載器,應用類加載也先不處理,交給我們的擴展類加載器,擴展類加載器也先不處理,交給我們的啟動類加載器,啟動類加載器沒辦法了,先嘗試加載一下吧,加載不了,回退給擴展類加載器,擴展類加載器也嘗試一下加載吧,加載不了再交給我們的應用類加載器,應用類加載器還是處理不了只有回退給我們的自定義加載器了。

  自定我們的類加載器,只要重寫com.sun.org.apache.bcel.internal.util包下的ClassLoader類的findClass方法,最後調用defineClass方法。就可以實現我們的自定義加載器。  

6.簡述老年代擔保機制

  答:https://www.cnblogs.com/cxiaocai/p/11520731.html

   Eden區滿了後,不會立即做MinorGC,會判斷一下老年代的剩餘空間是否大於我們要回收的對象,夠大就做MinorGC吧,如果老年代不夠大了,我們會判斷時候配置了-XX:-HandlePromotionFailure (jdk8以上默認設置),沒配置,直接FullGC,如果配置了就去判斷老年代的剩餘空間是否小於我們每次minorGC後每次要放在老年代對象大小的平均值,如果老年代小於minorGC了,那麼進行fullGC。

7.Java對象創建過程

  答:為對象分配存儲空間。開始構造對象。遞歸調用其超類的構造方法。進行對象實例初始化與變量初始化。執行構造方法體。使用完畢,計算GCRoot根,也就是對象不可達分析,進行垃圾回收,觸發finalize方法,方法運行結束,稍後進行垃圾回收。

8.類的生命周期

  答:類的完整生命周期包括7個部分:加載——驗證——準備——解析——初始化——使用——卸載

 9.如何判斷對象可以被回收?

  答:1,引用計數法(基本不用,循環引用對象永遠無法銷毀,可能內存溢出)

    2,可達性分析算法。GCRoots根節點一般為線程棧的本地變量、靜態變量、本地方法棧的變量等等。

    3,常見的引用類型(強軟弱虛)。

    4,finalize最終判斷對象存活。

    如何判斷一個類是無用的類

    1.該類所有的實例都已經被回收,也就是 Java 堆中不存在該類的任何 實例。

    2.加載該類的 ClassLoader 已經被回收。    
    3.該類對應的 java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。 

10.JVM的永久代中會發生垃圾回收么?

  答:永生代也是可以回收的,條件是

    1.該類的實例都被回收。

    2.加載該類的classLoader已經被回收

    3.該類不能通過反射訪問到其方法,而且該類的java.lang.class沒有被引用 當滿足這3個條件時,是可以回收,但回不回收還得看jvm有沒有“時間”。

11.一般用什麼來分析GC日誌分析

  答:jmap -heap PID,jstat -gc PID,javap -c ***.class,線上儘力別用jvisualvm命令,消耗性能,想快速優化(完全不熟悉業務)可以直接通過調整eden,Survivor區來設置,記得為什麼會發生fullGC,兩個重要的一個動態年齡計算,另一個是老年代擔保機制,熟悉業務時,可以結合業務來調整,而且在項目上線前,應該做提前的預估。

12.調優命令

  答:

  棧相關 

    -Xss->設置單個線程棧大小,比如-Xss512K,數值越小,一個線程棧里能分配的棧幀就越少,說明可以開啟的線程數越多 

  方法區(元空間)

    -XX:MetaspaceSize->設置方法區的大小,也是觸發GC的閾值,比如-XX:MetaspaceSize=256M

    -XX:MaxMetaspaceSiz->設置方法區的最大值,比如-XX:MaxMetaspaceSize=256M 

  堆相關

    -Xms->jvm啟動時分配的內存,比如‐Xms200m

    -Xmx->jvm運行過程中分配的最大內存,比如-Xmx500m

    -Xmn->設置年輕代大小,比如-Xmn2g

    -XX:NewSize->設置年輕代大小 比如-XX:NewSize=2g

    -XX:PretenureSizeThreshold->可以設置大對象的大小,比如-XX:PretenureSizeThreshold=100000000 單位為btye。

    -XX:MaxTenuringThreshold->設置分代年齡,比如-XX:MaxTenuringThreshold=10 默認為15。

    -XX:-HandlePromotionFailure->老年代分配擔保機制參數,1.8默認開啟。

    -XX:-UseAdaptiveSizePolicy->禁止JVM自動優化eden和Survivor默認比例8:1:1,反之JVM默認有這個參數-XX:+UseAdaptiveSizePolicy,會導致這個比例自動變化。

    -XX:SurvivorRatio->設置Eden和Survivor大小比如 -XX:SurvivorRatio =8,注意Survivor區有兩個。表示Eden:Survivor=8:2,一個Survivor區占整個年輕代的1/10。 

    -XX:NewRatio->設置老年代和年輕代的比值大小 比如-XX:NewRatio=4,表示年老代和年輕代比值為4:1。

  還有很多的命令就不依依列舉了,可以去看我的博客https://www.cnblogs.com/cxiaocai/p/11570519.html

13.Minor GC與Full GC分別在什麼時候發生?

  答:Minor GC發生在年輕代,當eden區滿的時候會出發Minor GC。

    Full GC發生在老年代,回收所有堆空間的內存,Minor GC前的老年代擔保機制可能出發FullGC。

14.簡述對象動態年齡判斷

  答:https://www.cnblogs.com/cxiaocai/p/11520731.html 

  當我們做完minorGC以後,對象放在to區域,也就是我們Survivor的to區域,可能對象是放不下的,這時會來計算分類年齡,大致是這樣來算的將所有分代年齡為1的相加,再加上分代年齡為2的,再加分代年齡為3的,依次相加,一直加到最大的分代年齡,但在相加過程中,你會發現加到分代年齡為m的對象,總大小已經放滿了to區域,這時就將m到n分代年齡的對象都移置到老年代,包含m。也就是大於Survivor區域的50%時,則後面的對象,包含該年齡的對象都放置在老年代。

15.類的實例化順序,比如父類靜態數據,構造函數,字段,子類靜態數據,構造函數,字段,他們的執行順序

  答:當創建類對象時,先初始化靜態變量和靜態塊,然後是非靜態變量和非靜態代碼塊,然後是構造器。由於靜態成員只會被初始化一次,所以如果靜態成員已經被初始化過,將不會被再次初始化。

16.簡述一下逃逸分析

  答:-XX:+DoEscapeAnalysis : 表示開啟逃逸分析。-XX:-DoEscapeAnalysis : 表示關閉逃逸分析 從jdk 1.7開始已經默認開始逃逸分析,如需關閉,需要指定-XX:-DoEscapeAnalysis。逃逸是指在某個方法之內創建的對象,除了在方法體之內被引用之外,還在方法體之外被其它變量引用到;這樣帶來的後果是在該方法執行完畢之後,該方法中創建的對象將無法被GC回收,由於其被其它變量引用。正常的方法調用中,方法體中創建的對象將在執行完畢之後,將回收其中創建的對象;故由於無法回收,即成為逃逸。

關注公眾號獲得更多面試和學習資料。