【JVM】JVM内存结构之——G1收集器

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 【JVM】JVM内存结构之——G1收集器,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文


1. 什么是G1

G1收集器(Garbage-First Garbage Collector) 整堆收集。
G1收集器是一款在server端运行的垃圾收集器,专门针对于拥有多核处理器和大内存的机器,在JDK 7u4版本发行时被正式推出,在JDK9中更被指定为默认GC收集器。它满足高吞吐量的同时满足GC停顿的时间尽可能短。

  1. 因为G1是一个并行/并发回收器,它把堆内存分割为很多不相关的区域(Region) (物理上 不连续的)。使用不同的Region来表示Eden、幸存者0(S0)区,幸存者(S1)1区,老年代等。
  2. 由于这种方式的侧重点在于回收垃圾最大量的区间(Region),所以我们给G1一个名字:垃圾优先(Garbage First) 。
  3. G1 GC有计划地避免在整个Java 堆中进行全区域的垃圾收集。G1跟踪各个Region 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
  4. G1收集器使用的是 整体上使用标记整理 两个Region 之间 标记复制算法。

2. G1收集器发展历程

1.2004发表:G1的第一篇paper
2.2012年才在jdk1.7u4中可用
3.2017 年jdk9中将G1变成默认的垃圾收集器以替代CMS。

3. G1收集器分区划分

1.使用G1收集器时,它将整个Java堆划分成约2048个大小相同的独立Region块,每个Region块大小根据堆空间的实际大小而定,整体被控制在1MB到32MB之间,且为2的N次幂,即1MB, 2MB, 4MB, 8MB, 1 6MB, 32MB。2048MB 每个独立区间1
2.可以通过-XXG1HeapRegionSize设定。所有的Region大小相同,且在JVM生命周期内不会被改变。
3.虽然还保留着新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region (不需要连续)的集合。通过Region的动态分配方式实现逻辑_上的连续。
一个region(分区)只能属于一个角色,有可能为eden区、S区、老年代等, E表示为Eden区、S区表示为S1,S0区,老年代O区 空白的表示为未使用的分配的内存。
H区存放巨型对象
在这里插入图片描述

3.1 为什么G1收集器需要设计巨型对象

  1. 在G1收集器中也有一个新的内存区域,称作为:Humongous (H)区(巨型对象),主要存放一些比较大的对象,一个对象大于region的一半时,称之为巨型对象,G1不会对巨型对象进行拷贝,回收时会考虑优先回收。
    2.在以前收集器中,如果是一个大对象是直接放入到老年代中,而触发老年代GC不是很频繁,万一该大对象不是非常频繁的使用,则会非常浪费我们堆内存,为了解决这个问题在G1收集器专门弄一个H区存放巨型对象,如果一个H区装不下的情况下,则会寻找连续H区存储,如果还没有足够的空间,有可能会引发FULLGC.

3.2 G1收集器参数设置

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

其中-XX:+UseG1GC为开启G1垃圾收集器,-Xmx32g 设计堆内存的最大内存为32G,-XX:MaxGCPauseMillis=200设置GC的最大暂停时间为200ms。如果我们需要调优,在内存大小一定的情况下,我们只需要修改最大暂停时间即可。

3.3 G1收集器回收的细节

G1收集器回收过程分为三个环节:

  1. 年轻代GC(Young GC )
  2. 新生代和并发标记过程( Concurrent Marking
  3. 混合回收(Mixed GC )—-
  4. Full GC
    young gc(新生代) 一> young gc + concurrent mark(新生代+并发标) 一> Mixed GC(混合回收)顺序,进行垃圾回收。

1.应用程序分配内存,当年轻代的Eden区用尽时开始年轻代回收过程; G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收。然后从年轻代区间移动存活对象到Survivor区间或者老年区间,也有可能是两个区间都会涉及。

2.当堆内存使用达到一定值(默认45%)时,开始老年代并发标记过程。—CMS

  1. 标记完成马上开始混合回收过程。对于一个混合回收期, G1 GC从老年区间移动存活对象到空闲区间,这些空闲区间也就称为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,G1的老年代回收器不需要整个老年代被回收,一次只需要扫描/回收一小部分老年代的Region就可以了。同时,这个老年代Region是和年轻代一起被回收的。

3.4 G1收集器Rset问题(记忆集)

1.概念:
在垃圾收集过程中,会存在一种现象,即跨代引用,在G1中,又叫跨Region引用。如果是年轻代指向老年代的引用我们不用关心,因为即使Minor GC把年轻代的对象清理掉了,程序依然能正常运行,而且随着引用链的断掉,无法被标记到的老年代对象会被后续的Major GC回收

2.如果是老年代指向年轻代的引用,那这个引用在Minor GC阶段是不能被回收掉的,那如何解决这个问题呢?

3.最合理的实现方式自然是记录哪些Region中的老年代的对象有指向年轻代的引用。GC时扫描这些Region就行了。这就是RSet存在的意义。RSet本质上是一种哈希表,Key是Region的起始地址,Value是一个集合,里面存储的元素是卡表的索引号。

4.现代JVM,堆空间通常被划分为新生代和老年代。由于新生代的垃圾收集通常很频繁,如果老年代对象引用了新生代的对象,那么,需要跟踪从老年代到新生代的所有引用,从而避免每次YGC时扫描整个老年代,减少开销。

5.对于HotSpot JVM,使用了卡标记(Card Marking)技术来解决老年代到新生代的引用问题。具体是,使用卡表(Card Table)和写屏障(Write Barrier)来进行标记并加快对GC Roots的扫描。

3.5 G1两种回收策略

G1 Young GC(新生代GC)
Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。

G1 Mix GC(混合回收)

4. G1收集器优缺点

  1. 并行与并发
    并行:G1收集器在回收时,可以实现多个GC线程同时执行 利用CPU多核利用率,但是会让用户线程暂停 触发stw机制。 堆6个GB JDK8环境开启G1 jdk9默认使用
    并发:多个GC与用户线程用时执行,用户线程不会阻塞。
  2. 分代收集原理
    G1收集器,也会分为新生代、老年代, 新生代eden、S0或者S1区域,但是不要求整个eden、S0或者S1区域具有连续性。
    与之前的收集器不同,它可以收集新生代也可以收集老年代。
  3. 空间整合
    之前我们所学习的CMS收集器采用标记清除算法,容易产生碎片化的问题且空间不连续性,而G1收集器划分成n多个不同的采用标记压缩算法,没有产生碎片化的问题。分配大对象的时候,避免FullGC的问题,所以如果堆内存空间比较大,使用G1收集器更加有优势。
  4. 可预测的停顿时间模型 能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
    由于G1收集器采用分区模型,所以G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制。G1跟踪各个Region 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。

缺点:小内存的情况下使用cms收集器,大内存的情况下可以使用G1收集器。G1收集器6GB以上

5. G1收集器核心配置参数

JDK9已经默认开启了G1收集器,如果在JDK8开启G1收集器。需要配置
-XX:G1HeapRegionSize 设置每个Region的大小。值是2的幂,范围是1MB 到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。默认是堆内存的1/2000。 也就是G1收集器最小堆内存应该是2GB以上,最大堆内存64GB
-XX:MaxGCPauseMillis 设置期望达到的最大Gc停顿时间指标 ,默认值是200ms
-XX:ParallelGCThread 设置垃圾回收线程数 最大设置为8
-XX:ConcGCThreads 设置并发标记的线程数。将n设置为并行垃圾回收线程数(ParallelGCThreads)的1/4左右。
-XX:InitiatingHeapOccupancyPercent 设置触发并发GC周期的Java堆占用率阈值。超过此值,就触发GC。默认值是45。
-XX:+UseG1GC 设置开启G1收集器

-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails -verbose:gc

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131182.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!