前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >G1垃圾收集器概述

G1垃圾收集器概述

作者头像
黑洞代码
发布2021-04-28 11:08:03
8990
发布2021-04-28 11:08:03
举报

前言

开始学习前,抛出两个常见面试问题:1.G1的回收原理是什么?为什么G1比传统的GC回收性能好?2.为什么G1如此完美仍然会有ZGC?简单的回顾下CMS垃圾回收机制,下面介绍了一个极端的场景(而且是经常发生的) 在发生Minor GC时,由于Survivor区已经放不下了,多出的对象只能提升(Promotion)到老年代。但是此时老年代因为空间碎片的缘故,会发生Concurrent mode failure的错误。这个时候,就需要降级为Serial Old垃圾回收器进行收集。这就是比concurrent mode failure 更加严重的promotion failed的问题。一个简单的Minor,竟然能演化成耗时最长的Full GC。最要命的是,这个停顿时间是不可预知的。有没有一种方法,能够首先定义一个停顿时间,然后反向推算收集内容呢?就像是领导在年初制定KPI一样,分配的任务多久多干些,任务少就少干点。类似需要徒步一段很长的路,然后在路中有多个里程碑,到达一个后可以休息一会。G1的思路说起来类似,它不要求每次都把垃圾清理的干干净净,只是努力做它认为对的事情。我们要求G1,在任意1秒的时间内,停顿不得超过10ms,这就是在给它制定KPI。G1会尽量达成这个目标,它能够推算出本次要收集的大体区域,以增量的方式完成收集。这也是使用G1垃圾回收器不得不设置的一个参数:-XX:MaxGCPauseMilis=10

简介

G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

为解决CMS算法产生空间碎片和其它一系列的问题缺陷,HotSpot提供了另外一种垃圾回收策略,G1(Garbage First)算法,通过参数-XX:+UseG1GC来启用,该算法在JDK 7u4版本被正式推出,官网对此描述如下:

The Garbage-First (G1) collector is a server-style garbage collector, targeted for multi-processor machines with large memories. It meets garbage collection (GC) pause time goals with a high probability, while achieving high throughput. The G1 garbage collector is fully supported in Oracle JDK 7 update 4 and later releases. The G1 collector is designed for applications that: •Can operate concurrently with applications threads like the CMS collector.•Compact free space without lengthy GC induced pause times.•Need more predictable GC pause durations.•Do not want to sacrifice a lot of throughput performance.•Do not require a much larger Java heap.

官网的上述翻译如下:

G1垃圾收集算法主要应用在多CPU大内存的服务中,在满足高吞吐量的同时,竟可能的满足垃圾回收时的暂停时间,该设计主要针对如下应用场景:

•垃圾收集线程和应用线程并发执行,和CMS一样•空闲内存压缩时避免冗长的暂停时间•应用需要更多可预测的GC暂停时间•不希望牺牲太多的吞吐性能•不需要很大的Java堆 (翻译的有点虚,多大才算大?)

为什么叫G1?

G1的目标是用来干掉CMS的,它同样是一款软实时垃圾回收器。相比CMS,G1的使用更加人性化。比如,CMS垃圾回收器的相关参数有72个,而G1的参数只有26个。G1的全称是GarbageFirst GC,为了达成上面制定的KPI,它和前面介绍的垃圾回收器,在对堆的划分上有一些不同。其它的回收器,都是对某个年代的整体收集,收集时间上自然不好控制。G1把堆切成了很多份,把每一份当作一个小目标,每一份收集时间自然是很好控制的。那么题又来了:G1有年轻代和老年代区分吗?

如图所示,G1也是有Eden区和Survivor区的概念的,只不过它们在内存上不是连续的,而是由一小份一小份组成的。这一小份区域的大小是固定的,名字叫做小堆区(Region)。小堆区可以是Eden也可以是Survivor区,还可以是Old区,所以G1的年轻代和老年代的概念都是逻辑上的。每一块Region,大小都是一致的,它的数值在1M-32M字节之间的一个2的幂值数。但假如我的对象太大,一个Region放不下怎们办?注意图中有一块很大的黄色区域,叫Humongous Region,大小超过Region 50%的对象,将会在这里分配。Region的大小,可以通过参数进行设置:-XX:G1HeapRegionSize=M 那么回收的时候,到底回收那些小堆区呢?是随机的吗?当然不是,事实上,垃圾最多的小堆区,会被优先回收,这也是G1名字的由来。

堆内存结构

CMS

以往的垃圾回收算法,如CMS,使用的堆内存结构如下:

1.新生代:eden space + 2个survivor2.老年代:old space3.持久代:1.8之前的perm space4.元空间:1.8之后的metaspace

以上这些space必须是地址连续的空间。

G1

在G1算法中,采用了另外一种完全不同的方式组织堆内存,堆内存被划分为多个大小相等的内存块(Region),每个Region是逻辑连续的一段内存,结构如下:

每个Region被标记了E、S、O和H,说明每个Region在运行时都充当了一种角色,其中H是以往算法中没有的,它代表Humongous,这表示这些Region存储的是巨型对象(humongous object,H-obj),当新建对象大小超过Region大小一半时,直接在新的一个或多个连续Region中分配,并标记为H。

Region

堆内存中一个Region的大小可以通过-XX:G1HeapRegionSize参数指定,大小区间只能是1M、2M、4M、8M、16M和32M,总之是2的幂次方,如果G1HeapRegionSize为默认值,则在堆初始化时计算Region的实际大小,具体实现如下:

默认把堆内存按照2048份均分,最后得到一个合理的大小。

GC模式

G1中提供了三种模式垃圾回收模式,young gc、mixed gc 和 full gc,在不同的条件下被触发。

young gc

发生在年轻代的GC算法,一般对象(除了巨型对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次young gc,这种触发机制和之前的young gc差不多,执行完一次young gc,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。

mixed gc

当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。

那么mixed gc什么时候被触发?

先回顾一下cms的触发机制,如果添加了以下参数:

代码语言:javascript
复制
-XX:CMSInitiatingOccupancyFraction=80 
-XX:+UseCMSInitiatingOccupancyOnly

当老年代的使用率达到80%时,就会触发一次cms gc。相对的,mixed gc中也有一个阈值参数 -XX:InitiatingHeapOccupancyPercent,当老年代大小占整个堆大小百分比达到该阈值时,会触发一次mixed gc.

mixed gc的执行过程有点类似cms,主要分为以下几个步骤:

1.initial mark: 初始标记过程,整个过程STW,标记了从GC Root可达的对象2.concurrent marking: 并发标记过程,整个过程gc collector线程与应用线程可以并行执行,标记出GC Root可达对象衍生出去的存活对象,并收集各个Region的存活对象信息3.remark: 最终标记过程,整个过程STW,标记出那些在并发标记过程中遗漏的,或者内部引用发生变化的对象4.clean up: 垃圾清除过程,如果发现一个Region中没有存活对象,则把该Region加入到空闲列表中

full gc

如果对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免full gc.

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 简介
  • 为什么叫G1?
  • 堆内存结构
    • CMS
      • G1
      • Region
      • GC模式
        • young gc
          • mixed gc
          • full gc
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档