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)是可以精准控制停顿。该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。

🍀 小总结

在这里插入图片描述

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