097_G1垃圾回收器的分区

6

G1我们要讲什么?

1G1的一些基本原理,比如,分区原理,对象分配原理,跨分区存储的原理。

2G1的一些设计思想,比如停顿预测模型,regionSize设计,对象快速分配设计等

3G1GC过程及原理,包括ygcmixedgcfullgc的一些流程、细节、源码等。

本节内容:

1G1垃圾回收器的简单介绍(垃圾优先回收器)

1)垃圾回收优先

G1垃圾回收器,也可以叫垃圾回收优先回收器(Garbage-FirstG1),一句话概括就是,这种垃圾回收器,会优先回收垃圾,不会等到空间全部占满, 然后进行回收。

2)停顿预测模型

停顿预测模型,预测一次回收可以回收的分区数量,以满足我们对停顿时间的要求

3)化整为零的分区机制

Par New + CMS 这种回收器的分区是,新生代,老年代,s区。

G1是,把一整个大块儿的内存,分成n个相同大小的分区(region)比较灵活,这种灵活可变的region机制,是G1能够做到控制停顿时间的核心设计。

 

2、传统的分代模型和G1的内存模型对比

1HeapRegion G1垃圾回收器内存管理的基本单位,也是最小单位。

分区类型:

新生代分区:Young Heap Region YHR

自由分区:Free Heap Region FHR

老年代分区:Old Heap Region OHR

大对象分区:Humongous Heap Region HHR

2ParNew +CMS G1的内存模型对比

优势:它不用做很多复杂分区管理的相关的东西。并且,小内存的时候,垃圾回收其实不会造成很大的停顿。

劣势:用ParNew +CMS ,给64GB的内存给JVMeden区可能直接,20-30GB,回收一次eden区要2-3s,请求都可能直接超时了,是不是就比较恐怖?

内存分代模型中,传统的分代模型是按照块儿状来做内存分配,这种分配管理的方式,会有一些不足。比如在大内存机器的场景下,会出现一次GC时间过长,导致stop the world时间较长,严重的情况下,会对用户体验造成比较大的影响。

 

针对大内存场景,诞生了G1这种垃圾回收器,G1管理内存的时候,化整为零,直接将内存块儿,分割成了n个小内存块儿,根据需求,动态的分配给新生代,老年代,大对象来使用,同时G1 会根据垃圾回收的情况动态改变新生代(包括老年代、大对象分区)的大小(region个数)。

比如,有对象需要分配的时候,就有可能会对新生代进行扩展--动态变化的意思,扩展(新增)几个region,也有可能是,减少一些region。垃圾回收,回收时间比较长,说明GC压力太大,这个时候,是不是可以考虑,少给一些分区。

保证回收和程序运行的一个平衡。

 

所以,我们第一个比较关键的知识点,就是和region,也就是分区有关。所以,我们来看看region分区相关的内容。

 

3G1如何设置HeapRegionHR)的大小?

1)手动式——通过参数设置 G1HeapRegionSize,默认为

Region的大小限制,范围,1-32MB,同时需要满足 1 2 4 8 16 32

2)启发式推断——G1本身通过算法自动计算 G1根据内存(堆内存大小、分区的个数)的情况自动计算出region大小

 

注意:region的大小,只能在1-32MB之间,不能小于1MB,也不能大于32MB,并且要满足是2n次方。即,12481632这几个值。如果指定的值不是这个范围内的值,G1会根据一定的算法规则自动调整。

 

4、思考:HR的大小会对垃圾回收造成什么影响?为什么要设置成1 2 4 8 16 32

1)如果Region过小,比如说,256KB 造成对象分配的性能问题

系统运行,创建的对象,一般也有可能是几十到几百KB

第一,找到一个可以使用的region难度增加

分配一个对象需要找region的次数就会越多。简单来讲,就是,大海捞针,创建对象洒洒水,找个region跑断腿……

第二,跨分区存储的概率增加

分配对象的时候,可能需要找多次region分区,分区越小,就说明,同样的内存大小,可以存储的对象太少,还可能会出现,大量的稍微大一点的对象,就超过了一个region的大小,那么就只能跨分区存储,一个对象,要用多个region去存储,这个时候,分配一个对象的开销还是会比较大。

2)如果Region过大,比如说,64MB128MB256MB?造GC性能问题

第一:region回收价值的判定很麻烦。

Region一大块儿内存,做回收性价比判断?肯定比小region要难度大

第二:回收的判定过程会更加复杂。

GC ROOTs,追踪标记对象,然后标记垃圾对象,遇到跨代,跨区的存储,还要做一些额外的其他处理,导致回收需要的时间更长,在做垃圾回收的时候,是不是需要判断那些对象,需要回收。这个过程就会更加复杂。

 

为了平衡分配效率和回收的效率。在保证两者的性能的同时,设置一个合理的region分区大小。

3)为什么要设置成 1 2 4 8 16 32这几个值的范围?2n次方

第一,造成内存碎片,内存浪费的问题

一般内存的分配都是,几个G,比如2048 MB4096MB,或者8G 32G这种的,如果搞一个region = 3MB15MB23MB,有多少个分区,不能整除?是不是有可能分区数量不是整数?这是不是有可能导致内存碎片,有一部分没有利用上?扩展内存,也是按照2的倍数去扩展的。底层内存是不是按照字节为单位,位这个东西。

第二,计算机底层无法利用2进制计算速度快的特性

并且,计算机底层我们都知道是二进制的,如果使用非2n次幂的数,在region数量计算,自动扩展region数量的时候,除了内存碎片,有可能也无法利用计算机底层2进制计算的速度快的特性。(位运算速度非常快。)

2 * 2   ,1 << 2来做计算。

设置一个比较合理的region大小很重要。如何设置呢,让他能够计算出一个比较合理的值?

5region的大小到底是如何计算的?

1)堆分区的个数  默认2048个,这个数字会根据具体的内存大小自动计算

计算regionsize的时候,会直接用这个默认的2048这个值。

2)堆内存的大小  默认最大96MB,最小为0MB

设置InitialHeapSize相当于设置Xms,设置MaxHeapSize相当于设置Xmx

 

3)计算公式:region_size = max((InitialHeapSize+MaxHeapSize)/ 2 / 2048,1MB)[1,32]最小值

 

4)举几个例子:

第一种,只指定region大小:

假如region大小设置为2MB,则G1的总内存大小为,2048 * 2MB = 4GB  分区个数2048

第二种,指定堆内存大小,且最大值等于最小值:

假如设置堆内存大小: Xms,Xmx,并且Xms = Xmx=32GB,则regionSize = max((32GB+32GB)/2 / 2048),1MB) = 16MB

第三种,指定堆内存大小,且最大值不等于最小值:

假如设置堆内存大小Xms,Xmx,并且Xms = 32GBXmx=128GB,则RegionSize = max((32GB+128GB)/2/2048,1MB) = 32MB。并且由于G1垃圾回收器会自动计算分区个数,在这个例子中,分区的个数范围在32GB/32MB=1024 ~ 128GB/32MB=4096之间。

 

假如设置堆内存大小Xms,Xmx,并且Xms = 64GBXmx=256GB,大家自己计算一下。

 

6regionSize如果不符合规则,G1是怎么处理的?

最后留一个问题,regionSize如果我设置成3MB,或者1.5MB64MB,不符合G1的限制条件,会怎么样?或者说,如果我的堆内存给的是3G,然后计算出来的regionSize是一个非2n次幂,G1会做什么处理?

会做动态调整regionsize