天天看點

Jvm記憶體面試問題總結(包括堆外記憶體) 2020Jvm記憶體面試問題總結(包括堆外記憶體)

Jvm記憶體面試問題總結(包括堆外記憶體)

逆水行舟,不進則退. 再不努力就被淘汰了. 

ps: 本文為純手打學習筆記 ,在摸索中學習 , 隻為分享知識 , 請勿随意轉載. 如有我了解錯誤的地方, 請各路大佬務必留言指導 . 

附一份word版分享連接配接.

https://download.csdn.net/download/whx217/13092323

順便安利兩個筆記軟體Typora 和 幕布. 隻用電腦端建議Typora, 感覺唯一的缺點就是沒有手機端. 幕布最大的優勢就是支援思維導圖. 

  • jvm中有哪幾塊記憶體?
    • 堆記憶體
      • 放對象
      • 所有線程共享
    • 棧記憶體
      • 放代碼的線程。
      • 線程獨有,自由有自己的棧記憶體。
    • 永久代(java8之前)
      • 放我們的類
      • 放常量池
    • 線程計數器
    • 本地方法棧
      • 放動态庫
  • 堆記憶體是如何配置設定的?
    • 年輕代(預設情況下eden區和s1 / s2區的比例是 8:1:1)
      • eden區
      • Survivor 區(兩個Survivor區大小完全一樣)
        • Survivor  1 區
        • Survivor   2 區
    • 年老代(預設和年輕代一樣)
  • java8之後對記憶體分布做了什麼改進?
    • 主要是 永久代裡面的變化
      • 本來永久代中放了一些類和常量池
      • java8以後永久代就沒有了
        • 将常量放入了堆裡面
        • 将類放入了metaspace中
      • metaspace是不占用jvm記憶體空間的,占用的是本地空間。
  • jvm是如何運作起來的?
    • 1 類加載器 先加載war、jar中的class,将我們的類加載到metaspace中
    • 2.spring容器會基于反射技術将metaspace中的類建立出一個執行個體bean。将bean加載到堆記憶體中。
    • 3. 當線程處理請求的時候,在自己線程獨享的記憶體空間中,每個方法拿到一個棧幀 ,局部變量全部放到棧幀裡面去,同時局部變量會去堆記憶體中引用一個對象,方法層層嵌套,進行壓棧。
  • jvm在什麼情況下會觸發gc垃圾回收?
    • 1. 如果說eden滿了,必然會觸發gc垃圾回收。youngGC 簡稱ygc
      • 此時回收沒有人引用的對象。
      • 靜态變量/局部變量引用不會被回收。
      • 被引用的對象不會被回收。
  • 常見的垃圾回收算法都有什麼?
    • 引用計數法
      • 通過引用計數器标記占記憶體中的變量對堆記憶體中變量的引用數量,當gc垃圾回收的時候,發現引用計數器中的值是0 ,則被回收.
      • 引用計數法的缺點 : 無法解決循環引用的問題.
    • 标記清除法
      • 降垃圾回收分成兩個階段, 标記階段和 清除階段
      • 标記階段,jvm會暫停jvm中的所有線程并開啟GC線程. 有一個最大的ROOT對象, 從ROOT對象開始,依次标記,最終沒有指向ROOT對象的對象 就是垃圾對象.
      • 清除階段 , 清除垃圾對象 .
      • 标記清除法優缺點: 解決的了引用計數算法中的循環引用的問題. . 但是每次GC都要掃描全部對象,并且要暫停所有線程,  效率較低 , 同時會産生記憶體碎片.
    • 标記整理法 (JDK1.8 老年代 GC垃圾回收)
      • 在标記清除算法上做了優化, 第一階段标記階段是一樣的.
      • 整理階段, 把所有被标記的對象全部壓縮到記憶體的一端.
      • 清理階段 , 清除所有未被整理的垃圾對象.
      • 優缺點: 解決了記憶體碎片化的問題, 但是比标記清除算法更加消耗cpu . 因為中間多了一步對對象的移動這個操作.
    • 複制算法(JDK1.8 年輕代 GC垃圾回收 Survivor區)
      • 兩塊記憶體大小一樣的記憶體空間,在使用的使用依次使用其中的一個,另一個清除.留做下次使用.如此循環.
      • 優缺點:在垃圾對象多的時候,效率高,并且在清理後記憶體無碎片. 但是垃圾對象較少的時候不适用,因為頻繁複制,消耗資源. 另一個缺點就是記憶體隻能使用50% .
    • 分代算法
      • 根據垃圾回收的特點選擇不同的算法.
      • 老年代 GC垃圾回收
      • 年輕代 GC垃圾回收  Eden + Survivor ( 0 / 1 ) 區
  • 年輕代垃圾回收算法?YGC
    • 對于年輕代而言,大部分對象的生存周期都很短。eden大部分對象都是垃圾!~
      • 對于年輕代的垃圾回收算法,是複制算法,每次垃圾回收,将沒有被回收的對象複制到一個s區,然後将eden區整個清空。完成一次young GC。下一次ygc 時,将eden區和剛剛的s區中的對象篩選一次,将篩選的結果複制存入空白是s區,此時将剛剛被篩選的eden區和s區中的對象全部清空。完成又一次yGC。總的說就是兩個s區和eden區進行配合重複完成ygc。
  • 什麼時候年輕代中的對象會跑到老年代中呢?
    • 1 . 對象熬過了很多次垃圾回收。大概是熬過15次垃圾回收。
    • 2 . 某次垃圾回收的時候,發現s區放不下的對象。直接放入老年代。
    • 3. 建立對象的時候,發現對象是大對象,直接放入老年代。防止ygc時反複移動這個大對象。大概大于100KB,都算作大對象。
  • 老年代的垃圾回收算法是什麼?OGC
    • 老年代中的對象很多都是被長期引用的,尤其是spring管理的各種bean。
    • 标記-清除算法有什麼缺點:缺點是容易出現很多的記憶體碎片。
    • 标記-整理算法,将老年代中的存活對象标記起來,移動到一起,存活對象壓縮到一片記憶體空間中,其餘的空間全部算垃圾對象,一起清理掉。解決了記憶體碎片産生的問題。完成了一次OGC。
  • 常用的垃圾回收器都有什麼?
    • 串行垃圾回收器  (DefNew)(老年代)

是指使用單線程進行垃圾回收,在垃圾回收時,隻有一個線程在工作,并且java應用中的所有線程都要暫停,等待垃圾回收完成. 這種現象稱為 STW (stop - the - world)

    • 并行垃圾回收器(ParNew)(年輕代)

在串行垃圾回收器上進行了改進,将串行改成了并行,性能更高.也會出現STW.隻是速度更快, 停頓的時間更短.

    • parallelGC垃圾回收器
      • 在parnew回收器的基礎上,又新增了系統吞吐量等相關參數.
      • 可自動調整堆記憶體配置設定, 但是不常用.
    • CMS垃圾回收器(針對老年代)
      • 優點; 在做垃圾回收的時候 ,  讓程式盡量較少STW.
      • 初始化标記 - 并發标記 - 預處理 - 最終标記 - 并發清理 - 調整堆大小 - 重置 - 初始化标記 ... ...
    • parnew (ygc) + cms(ogc)    jdk8以及以前。
    • [重要]G1 垃圾回收器 直接進行分帶回收 、 新版本主推。java9開始變為預設的垃圾回收器.。
      • 在做jvm性能調優的時候, 也建議使用G1垃圾回收. 能大大簡化性能調優.
        • 三種垃圾回收模式 : Young GC / Mixed GC /Full GC
        • 調優三大步:
          • 第一步: 開啟G1垃圾回收器
          • 第二步: 設定堆的最大記憶體
          • 第三步:設定最大的停頓時間
      • G1垃圾回收器的原理 :
        • 取消了年輕代和老年代的實體劃分 , 取而代之的一個邏輯上的劃分 (區域)Region.  這樣我們無需去設定每一個區域的大小.
        • YGC時 , 還是使用複制算法.
        • Remembered Set (已記憶集合)
          • 在YGC時,G1垃圾回收器是如何找到ROOT根對象的 ?ROOT根對象即有可能在年輕代也有可能在老年代。
        • G1 垃圾回收器的相關參數
          • -XX:UseG1GC
            • 使用G1 GC進行垃圾回收
          • -XX:MaxGCPauseMillis
            • 設定期望達到的最大GC停頓時間STW的名額,預設值是200毫秒.(jvm會去協調,但并不能保證完全做到)
          • -XX:G1HeapRegionSize=n
            • 設定G1區域的大小,也就是一個Region的大小 ,值是2的幂,範圍是1M-32M .目标是根據最小的java堆大小劃分出約2018個區域。
            • 預設值是堆記憶體的1/2000.
          • -XX:ParallelGCThread=n
            • 設定STW工作線程數的值 。将n的值設定為CPU處理器線程的數量。n的值與處理器線程數的數量相同,最多為8 。
          • -XX:ConcGCThreads=n
            • 設定并行标記的線程數。将n的值設定為ParallelGCThread的1/4左右比較合适。
          • -XX:InitiatingHeapOccupancyPercent=n
            • 設定觸發标記周期的java堆占用率門檻值。也就是觸發MxiedGC的标準,預設占用率是整個java堆的45%。
      • G1垃圾回收器官方給出的優化建議
        • 1   避免設定年輕代相關參數
          • 避免使用-Xmn選項或-XX:NewRatio等其他相關選項設定年輕代的大小,如果設定的話, 會覆寫掉G1垃圾回收器的相關參數,影響G1垃圾回收器的性能。因為G1垃圾回收器是把記憶體劃分成多個Region。
        • 2   暫停的時間目标不要太過于苛刻。
          • G1 GC的吞吐量的目标是90%應用程式,10%垃圾回收。如果STW時間過短,會影響吞吐量。
      • Mxied GC  是一個混合的GC 。
        • Mxied GC 在什麼時候出發?  當老年代的大小占整個堆大小的45%的時候, 會觸發Mxied GC 。
        • Mxied GC 的回收步驟。
          • 全局并發标記
          • 拷貝存活對象
    • 老年代的回收比年輕代回收平均慢10倍。
    • 在進行垃圾回收的時候,所有的線程都是在等待的。
  • 可視化GC日志分析工具
    • 列印GC日志需要設定的參數
      • -XX:+PrintGC  輸出GC日志
      • -XX:+PrintGCDetails 輸出GC詳細日志
      • -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準形式)
      • -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期形式 yyyyMMdd等)
      • -XX:+PrintHeapAtGC 在進行GC的前後列印出堆的資訊
  • 如何設定jvm參數?
    • 根據資料量,預估 eden區一秒會建立多少對象。
    • eden區滿時觸發YGC時,s區能有多少存活的對象。
    • 老年代多久會被填滿。
  • 如何檢查jvm運作情況?
    • 通過 jstat工具去分析jvm運作情況。
    • 主要觀察ygc的頻率,s區能否放得下。
    • 老年代幾次ygc後會被填滿。多久會觸發一次ogc。
    • 當老年代滿了以後,會觸發一次FGC,(full GC)同時包含YGC/OGC/metaspaceGC,全員GC。
    • 壓測,觀察QPS,觀察cpu
    • 如果gc過于頻繁,說明堆記憶體設定不夠。
  • 項目中JVM是如何優化的?
    • 需要對自己生産環境的jvm進行分析。
    • 壓測,每秒單機500并發。
    • 實際操作過才行!~
  • 如何排查和處理線上系統的OOM問題?Out Of Memory
    • 記憶體溢出
    • 設定參數,一旦發生OOM時,導出記憶體快照。
    • 通過記憶體快照,找出占用記憶體過大的對象是誰,在哪些代碼中。
  • 流程圖
    • Jvm記憶體面試問題總結(包括堆外記憶體) 2020Jvm記憶體面試問題總結(包括堆外記憶體)
  • 補充:堆外記憶體(off-heap)
  • 說說你對堆外記憶體的了解?
    • 通過堆外記憶體将資料發送出去。
    • 堆外記憶體的資料是jvm記憶體中拷貝出去的。
    • 如何使用堆外記憶體?
      • ByteBuffer buffer = ByteBuffer.allocateDirect(1024)
  • 堆外記憶體的優勢是什麼?
    • 可以把資料直接寫入到堆外記憶體中去,可以在發送資料的時候減少一次拷貝。
  • 堆外記憶體是如何進行配置設定和回收的?
    • DirectByteBuffer 從jvm堆記憶體中引用堆外記憶體。
      • 可以有多個DirectByteBuffer,去引用多個堆外記憶體。
    • 可以通過jvm參數設定你最大的堆外記憶體的大小
    • 如果去申請堆外記憶體時,剩餘的記憶體空間已經夠了,那麼 ,會經曆一次gc垃圾回收,回收掉那些沒用的DirectByteBuffer,釋放掉那些DirectByteBuffer引用的堆外記憶體。
    • 如果在嘗試9次之後,還是沒有堆外記憶體釋放。那麼抛出異常。
  • 堆外記憶體會記憶體溢出嗎?OOM
    • 會, 一直申請堆外記憶體,而沒有堆外記憶體釋放,在嘗試9次以後,就會抛出oom異常。