1. Java垃圾回收機制
1.1. Java垃圾的判斷
- 引用計數法
- 可達性分析
1.2. 回收算法
1.2.1. 標記清除
先標記再清除,會有很多碎片,連續空間不足,不足以分配大對象,從而直接gc
1.2.2. 複製算法
將內存分爲兩塊,存活的複製到另外區域,剩餘被視爲垃圾的一併回收
1.2.3. 標記整理算法
和標記清除類似,加上了整理,騰出連續空間
1.2.4. 分代垃圾回收
商業垃圾回收器都是用這種算法。根據對象存活週期的不同將內存劃分爲幾塊並採用不用的垃圾收集算法
1.3. 垃圾回收器
1.3.1. Serial(串行垃圾回收器)
虛擬機運行在 Client 模式下的默認新生代收集器,高效,極短的停頓,需要stop the
Word
1.3.2. Parnew
ParNew 收集器其實就是 Serial 收集器的多線程版本
- 並行(Parallel):指多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態。
- 併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不一定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另一個 CPU 上。
1.3.3. Parallel scavenge垃圾回收器
新生代收集器,它也是使用複製算法的收集器,又是並行的多線程收集器特殊之處在於:標則是達到一個可控制的吞吐量。
最大垃圾收集停頓時間的-XX:MaxGCPauseMillis
設置吞吐量大小的-XX:GCTimeRatio
如果把此參數設置爲 19,那允許的最大 GC 時間就佔總時間的 5%(即 1/(1+19)),默認值爲 99 ,就是允許最大 1%
-XX:+UseAdaptiveSizePolicy 打開之後態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱爲 GC 自適應的調節策略(GC Ergonomics)。
擬機總共運行了 100 分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%
1.3.4. CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。特點:併發收集、低停頓
整個過程分爲4個步驟,包括:
- 初始標記(CMS initial mark)
- 併發標記(CMS concurrent mark)
- 重新標記(CMS remark)
- 併發清除(CMS concurrent sweep)
初始標記、重新標記這兩個步驟仍然需要"Stop The World",整個過程中耗時最長的併發標記和併發清除過程收集器線程都可以與用戶線程一起工作。這個是一下導致缺點的原因(浮動垃圾)
缺點:
導致吞吐量降低(低停頓追求)、CMS 收集器無法處理浮動垃圾、產生空間碎片(追求低停頓,不整理)
1.3.5. G1垃圾回收(garbege first)
定位取代cms,收集器是當今收集器技術發展的最前沿成果之一
特點:
- 並行與併發
- 分代收集
- 空間整合(整體,標記整理,局部 複製)
- 可預測的停頓
如果不計算維護 Remembered Set 的操作,G1 收集器的運作大致可劃分爲以下幾個步驟:
- 初始標記(Initial Marking)
- 併發標記(Concurrent Marking)
- 最終標記(Final Marking)
- 篩選回收(Live Data Counting and Evacuation)
1.4. Gc日誌
1.4.1. Gc日誌
爲了方便用戶閱讀,將各個收集器的日誌都維持一定的共性,例如以下兩段典型的 GC 日誌:
最前面的數字33.125: 和 100.667: 代表了 GC 發生的時間,這個數字的含義是從 Java 虛擬機啓動以來經過的秒數。
GC 日誌開頭的 [GC 和 [Full GC 說明了這次垃圾收集的停頓類型,而不是用來區分新生代 GC 還是老年代 GC 的。
如果有 Full ,說明這次 GC 是發生了 Stop-The-World 的,例如下面這段新生代收集器 ParNew 的日誌也會出現 [Full GC(這一般是因爲出現了分配擔保失敗之類的問題,所以才導致 STW)。如果是調用 System.gc() 方法所觸發的收集,那麼在這裏將顯示 [Full GC(System)。
接下來的 [DefNew、[Tenured、[Perm 表示 GC 發生的區域,這裏顯示的區域名稱與使用的 GC 收集器是密切相關的,例如上面樣例所使用的 Serial 收集器中的新生代名爲 "Default New Generation",所以顯示的是 [DefNew。如果是 ParNew 收集器,新生代名稱就會變爲 [ParNew,意爲 "Parallel New Generation"。如果採用 Parallel Scavenge 收集器,那它配套的新生代稱爲 PSYoungGen,老年代和永久代同理,名稱也是由收集器決定的。
後面方括號內部的 3324K->152K(3712K)含義是GC 前該內存區域已使用容量 -> GC 後該內存區域已使用容量 (該內存區域總容量)。而在方括號之外的 3324K->152K(11904K) 表示 GC 前 Java 堆已使用容量 -> GC 後 Java 堆已使用容量 (Java 堆總容量)。
再往後,0.0025925 secs 表示該內存區域 GC 所佔用的時間,單位是秒。有的收集器會給出更具體的時間數據,如 [Times:user=0.01 sys=0.00,real=0.02 secs] ,這裏面的 user、sys 和 real 與 Linux 的 time 命令所輸出的時間含義一致,分別代表用戶態消耗的 CPU 時間、內核態消耗的 CPU 事件和操作從開始到結束所經過的牆鍾時間(Wall Clock Time)。
CPU 時間與牆鍾時間的區別是,牆鍾時間包括各種非運算的等待耗時,例如等待磁盤 I/O、等待線程阻塞,而 CPU 時間不包括這些耗時,但當系統有多 CPU 或者多核的話,多線程操作會疊加這些 CPU 時間,所以讀者看到 user 或 sys 時間超過 real 時間是完全正常的。
1.4.2. 垃圾回收邏輯
來自:
https://www.cnblogs.com/czwbig/p/11127159.html (垃圾回收日誌部分很不錯)