1. 程式人生 > >Java全面解讀垃圾回收機制

Java全面解讀垃圾回收機制

1. JVM垃圾回收機制與實現

  • 堆:所有的物件例項與陣列,GC堆,分為新生代與老年代
  • 棧:棧幀包含區域性變量表(基本資料型別 8種、物件引用型別)、運算元棧、動態連結、方法出口
  • 方法區:類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料,也成為永久代

一般說棧指的是 虛擬機器棧,或者說是虛擬機器棧中的區域性變量表

TLAB:本地執行緒分配緩衝,執行緒分配記憶體,現用TLAB分配,用完重新分配新的TLAB
可以設定是否啟用TLAB

Mark Word:物件頭:物件自身的執行時資料,雜湊嗎,GC分代年齡,鎖狀態,持有的鎖,偏向執行緒ID,偏向時間戳

HotSpot採用直接指標訪問,棧中直接指向物件的地址,物件移動時,需要改變棧中的reference

2、垃圾回收演算法

  • 標記清除演算法 Mark-sweep
    存在效率問題與空間問題(產生大量不連續的記憶體碎片)
  • 複製演算法(Copying):應用十分廣泛,將記憶體分為一塊較大的Eden和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。回收時,將Eden與Survivor複製到另一塊Survivor是哪個。
    預設Eden與Survivor的比例是8:1
  • 標記整理演算法(Mark-Compact) 適用於老年代,將所有存活的物件都向一端移動,然後直接清理掉端邊界以外的記憶體

3 垃圾回收的其他概念

  • Stop The World:從GC Roots進行可達性分析是,為了保持一致性,停頓所有的Java執行執行緒。就像時間凍結
  • 準確式GC:虛擬機器可以知道記憶體中某個位置的資料具體是什麼型別
  • OopMaps:用來記錄物件引用,HotSpot使用,類載入過程中,把物件內的偏移量上是什麼資料計算出來。
  • 安全點:SafePoint 既程式執行時並非在所有地方都能停頓下來,只有安全點才能停頓。程式長時間執行的特徵,例如:方法呼叫、迴圈跳轉、異常跳轉等
  • 搶先式中斷和主動式中斷:搶先式首先把所有的執行緒全部中斷,如果發現有執行緒不在安全點上,則回覆執行緒;主動式中斷:通過設定標誌,執行緒執行時輪詢標誌,發現標誌則自己中斷。
  • 安全區域(Safe Region):如果程式不執行的時候,就沒法中斷,所以需要安全區域,一段程式碼片段中,應用關係不發生變化,則為安全區域

4 垃圾回收器

  1. Serial:新生代,採用複製演算法,老年代採用標記整理演算法,單執行緒,需要Stop The Worls,Client模式下的預設新生代收集器
  2. ParNew:Serial的多執行緒版本,新生代採用複製演算法,老年代採用標記整理演算法。Server模式的首選新生代收集器,因為目前只有它與Serial能與CMS收集器配合工作,在多CPU>2的情況下,有更好的效能
  3. Parallel(並行) Scavenge:新生代收集器,複製演算法,關注吞吐量,吞吐量=執行使用者程式碼的時間/(執行使用者程式碼的時間+垃圾收集時間),適合在後臺運算而不需要太多互動的任務。可以設定吞吐量大小eg:GCTimeRatio,也成為吞吐量優先收集器。可以自動控制新生代的大小、Eden與Suvivor的比例、晉升老年代的年齡,自適應調節策略是Paralle Scavenge與Parnew的最大區別
  4. Serial Old:serial老年代版本,client模式下使用,可以為Paralle Scavenge收集器搭配使用,或者作為CMS的後備
  5. Parallel Old:Parallel Scavenge的老年代版本,標記整理演算法,注重吞吐量與CPU資源敏感,可以用優先使用Parallel Scavengen+Parallel Old
  6. CMS(Concurrent併發 Mark Sweep):一種獲取最短回收停頓時間為目標的收集器。目前大量使用在網際網路站或者B/S系統的服務端上,尤其重視服務的響應速度。基於標記-清除演算法

  7. image
4個步驟
1.初始標記 需要Stop the World標記一下GCRoots能夠直接關聯到的物件,速度很快
2.併發標記 進行GCRoots Tracing的過程,耗時較長,併發
3.重新標記 需要Stop the World為了修正併發標記期間因為使用者程式繼續運作
而導致標記產生變動的那一部分物件的標記記錄,比初始標記階段階段稍長,比並發標記時間短得多
4.併發清除:耗時較長,併發
5.併發重置 :這個階段,重置CMS收集器的資料結構,等待下一次垃圾回收。

CMS的優點:併發收集、低停頓
對CPU資源非常敏感,因為併發標記與併發清理的過程會佔用CPU,
預設啟動的回收執行緒數是(CPU數量+3)/4,當CPU 小於4時,佔用了不少於%25的CPU資源。
CPU越多,佔用降低,因此提出了ICMS(Incremental Concurrent Mark Sweep)增量式併發收集器

CMS無法處理浮動垃圾(Floating Garbage)因此可能出現Concurrent Mode Failure,
導致另一次Full GC的出現。
老年代使用達到一定標準,就會啟用CMS,JDK1.5預設%68,1.6預設%92,
如果執行期間預留的記憶體無法滿足程式需要,就會出現Concurrent Mode Failure,臨時啟用Serial OLD

由於使用標記-清除演算法,產生了大量空間碎片,
如果無法找到足夠大的連續空間來分配當前物件,不得不出發一下Full GC。


   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  1. G1 收集器 Garbage First。一款新的面向服務端的收集器

    並行與併發:充分利用多CPU來縮短Stop The World的時間
    分代收集:不需要其他收集器,可以獨立管理GC堆
    空間整合:整體上是標記-整理演算法,區域性上是複製演算法
    可預測的停頓:建立可預測的停頓時間模型
    
    通過將堆空間劃分成多個相等的獨立區域,新生代與老年代不再是物理隔離的,他們都是一部分Region的集合。
    
    有計劃的避免Full GC,G1跟蹤各個Region裡面的垃圾的價值大小
    (回收所獲得的的空間大小與回收所需時間的比值),
    在後臺維護一個優先列表。每次都根據允許的收集時間,優先回收價值最大的Region。
    1 初始標記 標記GCRoots 能夠直接關聯到的物件,並且修改TAMS
    2 併發標記 從GC roots進行可達性分析
    3 最終標記 修正標記期間因為使用者程式繼續運作而導致的標記變化
    4 篩選回收 制定回收計劃,進行回收,與CMS不同的是,篩選回收過程在最終標記後直接進行,需要Stop The world
    
    由於只回收一部分Region,時間是使用者可以控制的,而且停頓使用者執行緒將大幅度提高收集效率。
         
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

5新生代與老年代的劃分

  1. 物件有限分配在Eden,Eden沒空間則進行一次Minor GC(新生代GC)
  2. 長期存活的物件,進入老年代,預設為15次,初始為0,每經過一次Minor GC+1,預設15晉升老年代,也可以調整閥值
  3. 大於閥值 PretenureSizeTreshold的直接進入老年代,避免Eden與兩個Survivor產生大量的記憶體複製。
  4. 動態物件年齡判定:如果在Survivor中相同連年大小的總和大於Survivor的一般,年齡大於或者等於該年齡的物件直接進入老年代

    空間分配擔當:如果老年代最大可用的連續空間大於新生代所有的物件的總空間,Minor Gc則可以確保是安全的的,可以設定是否允許擔保失敗,如果允許,則嘗試進行MinorGC,否則進行FullGC。如果MinorGC存活物件過多,出現了HandlePromotionFailure,則在失敗後發起FullGC。

    一般允許擔保失敗,避免FullGC過於頻繁。

6 FullGC 、Minor Gc 、Major GC

Full==MajorGC

Minor GC:新生代垃圾回收,非常頻繁,一般速度比較快
Major Gc:老年代垃圾回收,經常會伴隨一次MinorGC,一般比MinorGC慢10倍以上

7哪些物件可作為GC Roots物件?

虛擬機器棧中應用的物件

方法區裡面的靜態物件

方法區常量池的物件

本地方法棧JNI應用的物件