1. 回收 1.1. 找到不使用的對象 1.2. 釋放它們的記憶體 1.3. 壓縮堆 1.4. 合在一起稱為回收 2. Throughput回收器 2.1. 工作細節比較簡單 2.1.1. 可以在同一個GC周期內完成回收 2.1.2. 在單次操作過程中回收新生代或老年代 2.2. Minor GC 2 ...
1. 回收
1.1. 找到不使用的對象
1.2. 釋放它們的記憶體
1.3. 壓縮堆
1.4. 合在一起稱為回收
2. Throughput回收器
2.1. 工作細節比較簡單
2.1.1. 可以在同一個GC周期內完成回收
2.1.2. 在單次操作過程中回收新生代或老年代
2.2. Minor GC
2.2.1. 當Eden空間被填滿時,新生代回收就會發生
2.2.2. 新生代回收會將所有的對象移出Eden空間
-
2.2.2.1. Eden空間一般是空的
-
2.2.2.2. 不認為它被壓縮了
2.2.3. 另一些被移到老年代
2.2.4. 還有大量對象因不再使用而被丟棄
2.3. Full GC
2.3.1. 老年代回收會將新生代中的所有對象釋放出來
2.3.2. 只有老年代中還剩下活躍引用的對象
2.3.3. 所有這些對象占用的空間都會被壓縮
-
2.3.3.1. 只占用老年代的開始部分
-
2.3.3.2. 其餘部分是空閑的記憶體空間
2.4. 優化
2.4.1. 圍繞停頓時間進行的
2.4.2. 權衡
-
2.4.2.1. 時間和空間的取捨
-
2.4.2.1.1. 更大的堆會消耗機器上更多的記憶體
-
2.4.2.1.2. 應用程式會有更高的吞吐量
-
-
2.4.2.2. 執行GC所需的時間長度
-
2.4.2.2.1. 增加堆的大小可以減少Full GC停頓的次數
-
2.4.2.2.2. GC時間較長,可能延長平均響應時間
-
2.4.2.2.3. 為了縮短GC停頓時間
-
2.4.2.2.3.1. 將更多的堆分配給新生代而不是老年代
-
2.4.2.2.3.2. 會增加老年代回收的頻率
-
2.5. 自適應大小
2.5.1. 重新調整堆(和代)的大小,以實現其停頓時間的目標
- 2.5.1.1. 並不總是堆越大就越好
2.5.2. 當給定的停頓時間目標不切實際時,應用程式的行為會更糟糕
2.5.3. -XX:MaxGCPauseMillis=N
-
2.5.3.1. 用於設定應用程式可接受的最大停頓毫秒數
-
2.5.3.2. 預設情況下,不設置這個標誌
-
2.5.3.3. 適用於Minor GC和Full GC
-
2.5.3.4. 如果使用了一個非常小的值,那麼應用程式最終會得到一個非常小的老年代
- 2.5.3.4.1. 導致JVM非常頻繁地執行Full GC,應用程式的性能會很糟糕
2.5.4. GC中花費總時間3%到6%的應用程式,運行效果相當好
2.5.5. -XX:GCTimeRatio=N
-
2.5.5.1. 設定你希望應用程式在GC上花費的時間(相對於應用程式線程應該運行的時間)
-
2.5.5.2. 標誌的預設設置很適用
-
2.5.5.2.1. 預設值是99
-
2.5.5.2.2. 在沒有其他目標的情況下,推薦時間比例設置為19(GC時間占5%)
-
-
2.5.5.3. ThroughputGoal=1-(1÷ (1+GCGCTimeRatio))
3. CMS回收器
3.1. 已廢棄
3.2. 3個基本操作
3.2.1. 回收新生代(同時暫停所有的應用程式線程)
3.2.2. 運行併發周期來清理老年代的數據
3.2.3. 如果有必要,執行Full GC來壓縮老年代
3.3. 對象會從Eden空間移到一個Survivor空間(Survivor空間滿了之後就移動到老年代)
3.4. 預設情況下,CMS不會回收元空間
3.5. 除非發生Full GC,否則永遠不會調整新生代的大小
3.6. CMS周期的停頓一般比新生代停頓短得多,特定的應用程式可能對這些額外的停頓並不敏感
3.7. 併發周期
3.7.1. 初始標記(initial mark)階段開始
- 3.7.1.1. 會暫停所有應用程式線程
3.7.2. 標記(mark)階段
- 3.7.2.1. 不會暫停應用程式線程
3.7.3. 預清理(preclean)階段
- 3.7.3.1. 和應用程式線程同時運行
3.7.4. 重新標記(remark)階段
- 3.7.4.1. 不是併發的,會暫停所有的應用程式線程
3.7.5. 可中止的預清理(abortable preclean)階段
-
3.7.5.1. 會等到新生代被占用50%後才開始
-
3.7.5.2. 階段的中止,這是停止這個階段的唯一方式
3.7.6. 清除(sweep)階段
3.7.7. 併發重置(reset)階段
- 3.7.7.1. 最後一個階段
3.8. 當發生新生代回收,但是老年代中並沒有足夠的空間容納預計要晉升的對象時,CMS會執行Full GC
3.8.1. 所有的應用程式線程都會被暫停
3.8.2. 該操作是單線程的
-
3.8.2.1. 它耗時如此之長的原因之一
-
3.8.2.2. 併發模式失敗隨著堆的增長而影響更大的原因之一
3.8.3. 這種併發模式失敗是CMS被廢棄的主要原因
3.9. 老年代中有足夠的空間容納晉升對象,但空閑空間是碎片化的,所以對象晉升還是會失敗
3.9.1. CMS無法晉升對象,因為老年代是碎片化的
3.9.2. 要晉升的記憶體量比CMS預期的要大,這種情況更少
3.10. CMS的日誌可能顯示了Full GC,但是沒有任何常規的併發GC消息
3.10.1. 當元空間被填滿並需要回收時,這種情況就會發生
3.10.2. CMS不會回收元空間,所以如果它被填滿了,就需要Full GC來丟棄任何未被引用的類
3.11. 優化
3.11.1. 確保不會發生併發模式失敗或晉升失敗
-
3.11.1.1. 避免併發模式失敗是實現CMS最佳性能的關鍵
-
3.11.1.2. 避免這些失敗的最簡單的方法(在可能的情況下)是增加堆的大小
3.11.2. 當執行新生代回收時,CMS計算出沒有足夠的空間容納這些將晉升到老年代的對象,所以會先回收老年代
3.11.3. 讓老年代空間更大一些,要麼調整新生代和老年代的比例,要麼增加更多的堆空間
3.11.4. 更頻繁地運行後臺線程
-
3.11.4.1. 提早開始併發周期
-
3.11.4.2. -XX:CMSInitiatingOccupancyFraction=N
- 3.11.4.2.1. 預設為70
3.11.4.2.1.1. CMS併發周期會在老年代被占用70%時開始
- 3.11.4.2.2. 不要將該值設置得低於堆中的活躍數據量,至少應加上10%到20%
-
3.11.4.3. -XX:+UseCMSInitiatingOccupancyOnly
- 3.11.4.3.1. 預設情況下false
-
3.11.4.4. 如果兩個都設置了,CMS就會只根據老年代的填充比例來決定何時啟動後臺線程
-
3.11.4.5. 此處的占用率是基於老年代的,而不是整個堆
3.11.5. 使用更多的後臺線程
-
3.11.5.1. -XX:ConcGCThreads=N
-
3.11.5.1.1. 增加後臺線程數量
-
3.11.5.1.2. ConcGCThreads = (3 + ParallelGCThreads) / 4
-
3.11.5.1.3. 能比G1 GC早一步增加ConcGCThreads的值
-