G1 垃圾收集器------你瞭解多少

🍀 以前收集器特點

📌 年輕代和老年代是各自獨立且連接的內存塊;

📌 年輕代收集使用單 Eden+S0+S1 進行復制算法;

📌 老年代收集必須掃描整個老年代區域;

📌 都是以儘可能少而快速地執行 GC 爲設計原則。

🍀 G1 是什麼

G1Garbage-First)收集器,是一款面向服務端應用的收集器
在這裏插入圖片描述
從官網的描述中,我們知道G1是一種服務器端的垃圾收集器,應用在多處理器和大容量內存環境中,在實現高吞吐量的同時,儘可能的滿足垃圾收集暫停時間的要求。另外,它還具有以下特性:

CMS 收集器一樣,能與應用程序線程併發執行。
整理空閒空間更快。
需要更多的時間來預測 GC 停頓時間。
不希望犧牲大量的吞吐性能。
不需要更大的Java Heap

G1收集器的設計目標是取代 CMS 收集器,它同 CMS 相比,在以下方面表現的更出色:
G1是一個有整理內存過程的垃圾收集器,不會產生很多內存碎片。
G1Stop The World(STW) 更可控,G1在停頓時間上添加了預測機制,用戶可以指定期望停頓時間。

CMS垃圾收集器雖然減少了暫停應用程序的運行時間,但是它還是存在着內存碎片問題。於是,爲了去除內存碎片問題,同時又保留 CMS 垃圾收集器低暫停時間的優點,Java7發佈了一個新的垃圾收集器 - G1垃圾收集器。

G1是在2012年纔有jdk1.7u4中可用。Oracle官方計劃在jdk9中將G1變成默認的垃圾收集器以替代 CMS。它是一款面向服務端應用的收集器,主要應用在多CPU和大內存服務器環境下,極大的減少垃圾收集的停頓時間,全面提升服務器的性能,逐步替換java8以前的CMS收集器。

主要改變是 EdenSurvivorTenured 等內存區域不再是連續的了,而是變成了一個個大小一樣的 region
每個 region1M32M 不等。一個region有可能屬於EdenSurvivor或者Tenured內存區域。

📌 特點

  • 1、G1 能充分利用多 CPU、多核環境硬件優勢,儘量縮短 STW。
  • 2、G1 整體上採用標記-整理算法,局部是通過複製算法,不會產生內存碎片
  • 3、宏觀上看G1之中不再區分年輕代和老年代。把內存劃分成多個獨立的子區域(Region),可以近似理解爲一個圍棋的棋盤。
  • 4、G1收集器裏面講整個的內存區都混合在一起了,但其本身依然在小範圍內要進行年輕代和老年代的區分,保留了新生代和老年代,但它們不再是物理隔離的,而是一部分Region的集合且不需要Region是連續的,也就是說依然會採用不同的GC方式來處理不同的區域。
  • 5、G1雖然也是分代收集器,但整個內存分區不存在物理上的年輕代與老年代的區別,也不需要完全獨立的 survivor(to space) 堆做複製準備。G1只有邏輯上的分代概念,或者說每個分區都可能隨G1的運行在不同代之間前後切換;

🍀 底層原理

📌 Region 區域化垃圾收集器

區域化內存劃片Region,整體編爲了一些列不連續的內存區域,避免了全內存區的GC操作。
核心思想是將整個堆內存區域分成大小相同的子區域(Region),在JVM啓動時會自動設置這些子區域的大小,
在堆的使用上,G1並不要求對象的存儲一定是物理上連續的只要邏輯上連續即可,每個分區的不會固定地爲某個代服務,可以按需在年輕代和老年代之間切換。啓動時可以通過參數 -XX:+G1HeapRegionSize=n 可指定分區大小(1MB~32MB,且必須是2的冪),默認將整堆劃分爲2048個分區。
大小範圍在1MB~32MB,最多能設置2048個區域,也即能夠支持的最大內存爲:32MB * 2048 = 65536MB = 64G內存

G1 將新生代,老年代的物理空間劃分取消了 G1 算法將堆劃分爲若干區域(Region)
在這裏插入圖片描述 在這裏插入圖片描述
                               G1算法將堆劃分爲若干個區域(Region),它仍然屬於分代收集器

這些Region的一部分包含新生代,新生代的垃圾收集依然採用暫停所有應用線程的方式,將存活對象拷貝到老年代或者Survivor空間。

這些Region的一部分包含老年代,G1收集器通過將對象從一個區域複製到另外一個區域,完成了清理工作。這就意味着,在正常的處理過程中,G1完成了堆的壓縮(至少是部分堆的壓縮),這樣也就不會有CMS內存碎片問題的存在了。
在G1中,還有一種特殊的區域,叫Humongous(巨大的)區域
如果一個對象佔用的空間超過了分區容量50%以上,G1收集器就認爲這是一個巨型對象。這些巨型對象默認直接回被分配在年老代,但是如果它是一個短期存在的巨型對象,就會對垃圾收集器造成負面影響。爲了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那麼G1會尋找連續的H分區來存儲。爲了能找到連續的H區,有時候不得不啓動 Full GC
⏳ 最大好處是化整爲零,避免全內存掃描,只需要按照區域來進行掃描即可。

📌 回收步驟

G1 收集器下的 Young GC

針對 Eden 區進行收集,Eden 區耗盡後會被觸發,主要是小區域收集 + 形成連續的內存塊,避免內存碎片

  • Eden 區的數據移動到 Survivor 區,假如出現 Survivor 區空間不夠,Eden區數據會部分晉升到 Old
  • Survivor 區的數據移動到新的 Survivor 區,部分數據晉升到 Old
  • 最後 Eden 區收拾乾淨了,GC 結束,用戶的應用程序繼續執行。
    在這裏插入圖片描述
    在這裏插入圖片描述

📌 4步過程

初始標記:只標記GC Roots能直接關聯到的對象
併發標記:進行GC Roots Tracing的過程
最終標記:修正併發標記期間,因程序運行導致標記發生變化的那一部分對象
篩選回收:根據時間來進行價值最大化的回收
形如:
在這裏插入圖片描述

🍀 Case 案例

package com.brian.interview.study.jvm.gc;

import java.util.Random;

/**
 * Copyright (c) 2020 ZJU All Rights Reserved
 * <p>
 * Project: JavaSomeDemo
 * Package: com.brian.interview.study.jvm.gc
 * Version: 1.0
 * <p>
 * Created by Brian on 2020/2/16 18:05
 */

/**
 * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseG1GC
 */
public class G1Demo {
    public static void main(String[] args) {
        String str = "hello";
        while (true) {
            str += str + new Random().nextInt(77777777) + new Random().nextInt(88888888);
            str.intern();
        }
    }
}

🍀 常用配置參數(瞭解)

開發人員僅僅需要聲明以下參數即可:

三步歸納:開始G1+設置最大內存+設置最大停頓時間

-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=n:最大GC停頓時間單位毫秒,這是個軟目標,JVM將盡可能(但不保證)停頓小於這個時間

📌 -XX:+UseG1GC

📌 -XX:G1HeapRegionSize=n:設置的G1區域的大小。值是2的冪,範圍是1MB到32MB。目標是根據最小的Java堆大小劃分出約 2048 個區域

📌 -XX:MaxGCPauseMills=n:最大GC停頓時間,這是個軟目標,JVM將盡可能(但不保證)停頓小於這個時間

📌 -XX:InitiatingHeapOccupancyPercent=n:堆佔用了多少的時候就觸發GC,默認爲45

📌 -XX:ConcGCThreads=n:併發GC使用的線程數

📌 -XX:G1ReservePercent=n:設置作爲空閒空間的預留內存百分比,以降低目標空間溢出的風險,默認值是10%

🍀 和 CMS 相比的優勢

比起 CMS 有兩個優勢:

  • 1)G1不會產生內存碎片。
  • 2)是可以精準控制停頓。該收集器是把整個堆(新生代、老年代)劃分成多個固定大小的區域,每次根據允許停頓的時間去收集垃圾最多的區域。

🍀 小總結

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章