JVM系列【6】GC與調優1

JVM系列筆記目錄

  • 虛擬機的基礎概念
  • class文件結構
  • class文件載入過程
  • jvm記憶體模型
  • JVM常用指令
  • GC與調優

GC基礎知識

  • 什麼是垃圾

​ 沒有任何引用指向的一個對象或多個對象(循環引用)

file

  • 如何找到垃圾

    • 引用計數(ReferenceCount),缺點:無法解決循環引用
    • 根可達演算法(RootSearching),從根開始查找,找到對象是有用的,找不到的對象為垃圾。

file

什麼是根? 注意結合上一篇部落格 JVM系列【5】JVM常用指令-運行時數據區進行理解。

  1. 執行緒棧變數 JVM Stack、native method stack
  2. 靜態變數 static refercences in method area、Clazz
  3. 常量池 runtime cosntant pool
  4. JNI指針 c/c++ 指針
  • 常見的垃圾回收演算法

    1. 標記清除(mark sweep)

    缺點:位置不連續,產生碎片,效率偏低(需要進行兩遍掃描)

    適用情況:演算法相對簡單,適用於存活對象比較多的情況
    file

    1. 拷貝演算法 (copying)

      說明:沒有碎片,浪費空間,複製移動對象,需要調整對象引用

      適用情況:適用於存活對象較少的情況 只掃描一次

      file

    2. 標記壓縮(mark compact)

      說明:沒有碎片,效率偏低(兩遍掃描,指針需要調整)

    file

  • JVM堆記憶體分代模型(用於分代垃圾回收演算法)

    堆記憶體分代模型是部分垃圾回收器使用的模型,除Epsilon ZGC Shenandoah 之外的GC都使用邏輯分代模型,但是G1 是邏輯分代 物理不分代,除此之外都是邏輯分代 而且物理分代。

    file

    分代模型中,分新生代和老年代,比例是1:2

    1. 新生代 = Eden區+ 2個survivor區

      YGC回收後,大多數的對象會被回收,活著的進入s0;

      再次YGC,活著的對象eden+s0 進入s1;

      再次YGC,活著的對象eden+s1 進入s0;

      年齡足夠進入老年代,一般垃圾回收器是15,CMS是6;為什麼一般垃圾回收器分代年齡是15?參考下 JVM系列【4】記憶體模型-對象頭的內容有哪些,對象頭中4位標識GC年齡,所以最大的年齡是2^4 -1 = 15;

      s 區裝不下,直接進入老年代。

    2. 老年代: 存放一些"頑固對象",老年代滿了或是分配不下了會觸發FCG。

    3. 永久代(Permanent Generation,JDK1.7以前)/元數據區(MetaSpace,JDK1.8以後):存放的是Class文件結構,永久代必須指定大小限制 ,元數據可以設置也可以不設置,無上限(受限於物理記憶體),字元串常量在1.7以前存放在永久代,1.8以後存放在堆中。

  • 垃圾回收類型和對象分配過程

    1. 垃圾回收類型

      上一個點解釋了堆記憶體分代模型,這裡統一下概念:MinorGC=YGC指的是新生代即Y區的垃圾回收,MajorGC=FGC指的是老年代分配不下或是滿了發生的GC,包括新生代和老年代。

    2. 對象如何分配

      在之前的部落格中 JVM系列【4】記憶體模型-new Object()面試6連問-對象如何分配,簡單提過這一點。

file

​ 對照流程圖,分配過程:

	> 1. new對象後優先在棧上分配,棧上分配的對象`pop`後就消失;
	> 2. 是否是大對象(Y區分配不下的對象)?是就分配到O區,O區發生FGC後回收
	> 3. 不是大對象,優先分配TLAB(Thread Local Allocation Buffer執行緒本地分配快取)上,分配不下就分配到Eden區 。
  1. 了解棧上分配、TLAB、分配擔保機制和升代

    1. 哪些對象棧上分配?

      執行緒私有對象 、無逃逸對象即離開程式碼塊就沒有引用的對象、支援標量替換的對象,如類中變數可以用基本變數替換。

    2. 執行緒本地分配Thread Local Allocation Buffer
      獨佔eden空間,默認1%;多執行緒時候不用競爭eden就可以申請空間,提高效率;可分配小對象

    3. 分配擔保機制

      在新生代無法分配記憶體的時候,把新生代的對象轉移到老生代,然後把新對象放入騰空的新生代。參考

    4. 對象何時進入老年代?

      超過MaxTenuringThreshold指定的次數;

      根據動態年齡計算進入老年代,Survivor空間中年齡從小到大的對象進行累加,當加入某個年齡段後,累加和超過survivor區域*TargetSurvivorRatio(默認50%)的時候,就從這個年齡段往上的年齡的對象進行晉陞到老年代。參考

  • 常見的垃圾回收器

    常見的垃圾回收器有Serial/SerialOld、ParallelScavenge/ParallelOld、ParNew/CMS、G1、ZGC、Shenandoah、Epsilon。Serial/SerialOld、ParallelScavenge/ParallelOld、ParNew/CMS是分代的垃圾回收器,G1是邏輯上分代物理上不分代,ZGC和Shenandoah是不分代,Epsilon是JDK調試用的垃圾回收器。

    歷史:JDK誕生,Serial追隨,為了提供效率誕生PS,為配合CMS,誕生PN,CMS是1.4後期引入的,CMS是里程碑式的GC,但是CMS毛病比較多,因此目前沒有任何一個JDK版本默認CMS。

file

  • Serial

    年輕代 串列回收

    file

  • SerialOld

    老年代 串列回收

    file

  • PS(ParallelScavenge)

    年輕代 並行回收

    file

  • PO(ParallelOld)

    老年代 並行回收

    file

  • PN(ParNew)

    年輕代 增強版PS配合CMS的並行回收。PN 和 PS區別?PN 響應時間優先;PS吞吐量優先

  • CMS(ConcurrentMarkSweep)

    老年代並發的,垃圾回收和應用程式同時運行,降低STW的時間(200ms);

    CMS問題比較多,所以現在沒有一個版本默認是CMS,只能手工指定;

    CMS既然是MarkSweep,就一定會有碎片化的問題,碎片到達一定程度,CMS的老年代分配對象分配不下的時候,使用SerialOld 進行老年代回收,STW無法忍受;

    • 如何解決碎片化:設定-XX:+UseCMSCompactAtFullCollection 默認開啟;設定-XX:CMSFullGCsBeforeCompaction,默認為0 指經過多少次FGC才進行壓縮。
    • 浮動垃圾問題解決?降低觸發CMS的閾值,保持老年代有足夠的空間;參數-XX:CMSInitiatingOccupancyFraction 指定使用CMS時老年代使用了指定閾值的記憶體後觸發FGC,建議68-92%

    使用的演算法:三色標記+Increamental Update

  • G1

    STW可以達到10ms

    演算法:三色標記+SATB

  • ZGC

    STW號稱可以達到1ms

    演算法:顏色指針ColoredPointers + LoadBarrier

  • Shenandaoh

    演算法:ColoredPointers + WriteBarrier

  • 垃圾回收器和記憶體大小的關係

    Serial 幾十兆

    PS 上百兆-幾個G

    CMS 20G左右

    G1 上百G

    ZGC 4T-16T(JDK13可以支援)

知識分享,轉載請註明出處。學無先後,達者為先!

Tags: