1. 程式人生 > >JVM之垃圾回收(GC)

JVM之垃圾回收(GC)

垃圾回收

java會對記憶體進行自動分配與回收管理, 使上層業務更加安全, 方便的使用記憶體實現程式邏輯, 在不同的JVM實現及不同的回收機制中, 堆記憶體的劃分方式是不一樣的

垃圾回收的主要目的是清楚不再使用的物件, 自動釋放記憶體

問題: GC是如何判斷物件是否可以被回收的呢? 答: 為了判斷物件是否存活, JVM引入了GC Roots, 如果一個物件與GC Roots之間沒有直接或間接的引用關係, 比如某個失去了任何引用的物件, 或者兩個互相環島狀迴圈引用的物件等, 判決這些物件’死緩’是可以被回收的

問題: 什麼物件可以作為GC Roots呢? 答: 類靜態屬性中引用的物件, 常量引用的物件, 虛擬機器棧中引用的物件, 本地方法棧中引用的物件等 以上可以理解為判斷物件是否存活的標準

瞭解一下垃圾回收的相關演算法

  • 1.最基礎的為"標記-清除演算法", 該演算法會從每個GC Roots出發, 依次標記有引用關係的物件, 最後將沒有被標記的物件清除, 但是這種演算法會帶來大量的空間碎片, 導致需要分配一個較大連續空間時容易觸發FGC, 為了解決這個問題, 又提出了第二種演算法
  • 2.“標記-整理演算法”, 該演算法類似計算機的磁碟整理, 首先會從GC Roots出發標記存活的物件, 然後將存活物件整理到記憶體空間的一端, 形成連續的已使用空間, 最後把已使用空間之外的部分全部清理掉
  • 3.“Mark-Copy演算法”, 為了能夠並行的標記和整理將空間分為兩塊, 每次只啟用其中一塊, 垃圾回收時只需要把存活的物件複製到另一塊未啟用空間上, 將未啟用空間標記為已啟用, 將已啟用空間標記為未啟用, 然後清除原空間中的原物件

堆記憶體空間分為較大的Eden和兩塊較小的Survivor, 每次只使用Eden和Survivor區的一塊, 這種情況下, 使用"Mark-Copy"演算法減少了記憶體空間的浪費, “Mark-Copy” 現作為主流的YGC演算法進行新生代的垃圾回收

垃圾回收(Garbage Colletor)是實現垃圾回收演算法並應用在JVM環境中的記憶體管理模組, 當前實現的垃圾回收器有數十種, 以下介紹幾個列子:

  • Serial回收器是一個主要應用於YGC的垃圾回收器, 採用序列單執行緒的方式完成GC任務, 其中"Stop TheWorld"簡稱STW, 即垃圾回收的某個階段會暫停整個應用程式的執行,FGC的時間相對較長, 頻繁FGC會嚴重影響應用程式的效能

  • CMS回收器(Concurrent Mark Sweep Collector)是回收停頓時間比較短, 目前比較常用的垃圾回收器,它通過初始標記(Initial Mark), 併發標記(Concurrent Mark), 重新標記(Remark),併發清除(ConcurrentSweep)四個步驟完成垃圾回收工作,初始標記和重新標記階段依然會引發STW(STW即垃圾回收的某個階段會暫停整個應用程式的執行),而併發標記和併發清除兩個階段可以和應用程式併發執行, 也是比較耗時的操作, 但並不影響應用程式的正常執行

  • Hotspot在JDK7中推出了新一代G1(Garbage-First Garbage Collector)垃圾回收,通過-XX:+UseG1GC引數啟用, 和CMS相比, G1具備壓縮功能, 能避免碎片問題, G1的暫停時間更加可控, 效能不錯

詳解G1: G1將java堆空間分割成了若干相同大小的區域, 即region, 包括Eden, Survivor, O1d, Humongous四種類型, 其中, Humongous是特殊的O1d型別, 專門放置大型物件, 這樣的劃分方式意味著不需要一個連續的記憶體空間管理物件, G1將空間分為多個區域, 優先回收垃圾最多的區域, G1採用"Mark-Copy"演算法, 有非常好的空間整合能力, 不會產生大量的空間碎片, G1的一大優勢在於可預測的停頓時間, 能夠儘可能快的在指定時間內完成垃圾回收任務, 在JDK11中, 已經將G1設為預設垃圾回收器, 通過jstat命令可以檢視垃圾回收情況