JVM-垃圾收集器G1 & ZGC详解 & 安全点与安全区域(5)

G1收集器(-XX:+UseG1GC)

G1 垃圾回收器主要针对的配备多核处理器和大容量内存的机器,它极低降低了GC停顿时间,又有高吞吐量的性能

G1收集器内部结构

在这里插入图片描述
G1收集器将Java堆划分为多个大小相等的独立区域(Region),JVM最多可以有2048个Region。 一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数"- XX:G1HeapRegionSize"手动指定Region大小,但是推荐默认的计算方式,保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合,另外G1还设计的H区,这个区域专门存放大对象,在G1中所谓的大对象就是超过一个Region的50%就被称之为大对象,这样的对象会直接进入H区,为什么要这样设计,这样可以节省Old区的空间,在FullGC触发时也会将H区进行回收,E区和S区的占比还是8:1:1,一个Region它的状态有不确定性,这个在某个时间段是年轻代,过一段时间可能变成了老年代。

G1收集GC步骤:

在这里插入图片描述

初始标记:
标记GC ROOTS ,会STW 但速度快 和CMS的初始标记一样

并发标记:
与CMS的并发标记一样
最终标记:
同CMS的重新标记
筛选回收:
存在STW,这个阶段G1会对需要回收的区域进行回收价值和成本进行排序,根据用户所期望的GC停顿时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划, 例如:用户指定的停顿时间是2m,这个时候有两个块区域,A区域回收时间是2m可以回收0.5MB的空间,B区域回收时间也是2m但是回收的空间是2MB这样G1会优先回收B区域,这样性价比更高,G1采用复制算法回收几乎不会有太多内存碎片

G1垃圾收集分类

YoungGC:
YoungGC 在G1中不是Eden区满了就会立马进行,而是会判断回收Eden区时间,如果Eden区回收的时间远远小于 目标暂停时间,则不会进行回收,会新增Eden区域用来存在新增的对象,如果等于目标暂停时间才会触发
MixedGC:
不是FullGC,是根据-XX:InitiatingHeapOccupancyPercent设定值来进行触发的,MixedGC会回收Eden、部分Old以及H区(也是会根据回收的性价比排序来进行回收),如果在回收将存活的对象复制到剩余的空间而剩余的空间装不下,这样才会触发FullGC
Full GC:
停止系统程序,采用单线程进行标记、清理和压缩整理,清理出来一批Region来供下一次MixedGC使用,非常耗时

G1收集器参数设置

-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个 年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold:最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合 收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能 就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这 个值,存活对象过多,回收的的意义不大。
-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一 会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都 是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清 理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立 即停止混合回收,意味着本次混合回收就结束了。
G1垃圾收集器优化建议:
设置目标停顿时间不用太长也不能太短,太长会导致触发YoungGC时进行S区域的对象会很多,这样可能会触发动态年龄判断机制,导致有大批的对象进入old区域,这样会频繁的发生MixedGC
什么场景适合使用G1

  1. 50%以上的堆被存活对象占用
  2. 对象分配和晋升的速度变化非常大
  3. 垃圾回收时间特别长,超过1秒
  4. 8GB以上的堆内存(建议值)
  5. 停顿时间是500ms以内

ZGC收集器(-XX:+UseZGC)

一款求极致低延迟的垃圾收集器,ZGC 可以将STW降低在不超10ms(JDK16已经达到不超过1ms),它支持的内存达到了TB级,不分代(暂时),

ZGC中的内存布局

在这里插入图片描述
ZGC中就没有了分代的概念了,它将内存分成了一个一个的页面(page),
它里面有三种类型页面:
小页面:
指的是2MB的页面空间
中页面:
指的是32MB的页面空间
大页面:
指的是大于32MB的页面空间
在创建对象时存放到内存时先会判断对象的大小,如果小于256kb会放入小页面,如果大于256kb小于4Mb会放入中页面,如果大于4MB会放入大页面,ZGC针对不同的页面所采取的回收策略也不同,小页面优先回收,中页面、大页面尽量不回收
为什么这么设计?
标准大页(huge page)是Linux Kernel 2.6引入的,目的是通过使用大页内存来取代传统的4KB内存页面,以 适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能。 Huge pages 有两种格式大小: 2MB 和 1GB , 2MB 页块大小适合用于 GB 大小的内存, 1GB 页块大小适合用于 TB 级别的 内存; 2MB 是默认的页大小。 所以ZGC这么设置也是为了适应现代硬件架构的发展,提升性能
ZGC支持NUMA(了解即可)
在过去,对于X86架构的计算机,内存控制器还没有整合进CPU,所有对内存的访问都需要通过北桥芯片来完 成。X86系统中的所有内存都可以通过CPU进行同等访问。任何CPU访问任何内存的速度是一致的,不必考虑不 同内存地址之间的差异,这称为“统一内存访问”(Uniform Memory Access,UMA)。UMA系统的架构示 意图如图所示。 在UMA中,各处理器与内存单元通过互联总线进行连接,各个CPU之间没有主从关系。之后的X86平台经历了 一场从“拼频率”到“拼核心数”的转变,越来越多的核心被尽可能地塞进了同一块芯片上,各个核心对于内 存带宽的争抢访问成为瓶颈,所以人们希望能够把CPU和内存集成在一个单元上(称Socket),这就是非统一 内存访问(Non-Uniform Memory Access,NUMA)。很明显,在NUMA下,CPU访问本地存储器的速度比 访问非本地存储器快一些。下图所示是支持NUMA处理器架构示意图。
在这里插入图片描述

ZGC是支持NUMA的
在进行小页面分配时会优先从本地内存分配,当不能分配时才会从远端的内存分配。对 于中页面和大页面的分配,ZGC并没有要求从本地内存分配,而是直接交给操作系统,由操作系统找到一块能 满足ZGC页面的空间。ZGC这样设计的目的在于,对于小页面,存放的都是小对象,从本地内存分配速度很 快,且不会造成内存使用的不平衡,而中页面和大页面因为需要的空间大,如果也优先从本地内存分配,极易 造成内存使用不均衡,反而影响性能。
在这里插入图片描述

ZGC核心技术(指针着色技术)

现有的计算机大部分的都是64位机,64位机可以虚拟出来的内存可以达到TB级,可以支撑未来很长时间计算机对内存的需要,而指针着色技术只能在64位机上进行,并且因为要求64位的指针,也就不能支持压缩指针,
在这里插入图片描述

通过上面图中可以看到64位的虚拟地址空间,在前42位就可以虚拟出4TB的内存空间出来,指针着色技术是借用42-45的位置进行打标记,例如,在42的位置上标记成1 表示成绿色,在43的位置上标记成1 表示成红色,在45的位置上标记成1 表示成蓝色,这样就可以该对象在GC中正处于什么状态,快速实现垃圾回收中的并发标记、转移和重定位等

ZGC GC流程

在这里插入图片描述
初始标记:
从根集合(GC Roots)出发,找出根集合直接引用的活跃对象(根对象
并发标记:
根据初始标记找到的根对象,使用深度优先遍历对象的成员变量进行标记
再标记:
这个阶段需要暂停(STW),主要处理漏标对象,通过SATB算法解决(G1中的解决漏标的方案)。
并发转移准备:
分析最有价值GC分页<无STW >
初始转移:
转移初始标记的存活对象同时做对象重定位<有STW>
并发转移:
对转移并发标记的存活对象做转移<无STW>

ZGC基于指针着色的标记和转移算法

1.所有的新生成的对象或者是GC已经做完了对象的指针状态都是Remapped (重定位)
在这里插入图片描述2.初始标记阶段由于只标记了GC ROOTs,所以指向A对象的指针颜色变成了M0
在这里插入图片描述

3.并发标记阶段,是标记GC Root 所有与之有关的对象,所以指向B的指针颜色也变成了M0(绿色)
在这里插入图片描述

4.再标记阶段,主要处理漏标对象,如果新增了D对象,通过对A的可达性算法能遍历到D,那么它的指针颜色就变成了M0的状态,如果没有遍历到那么它的颜色就是Remapped (蓝色)
在这里插入图片描述
5.并发转移准备,分析最有价值GC分页,如果整个小页面全部都是垃圾在该阶段也会直接回收掉
在这里插入图片描述
6.初始转移,转移初始标记的存活对象同时做对象重定位
在这里插入图片描述
7.并发转移,对转移并发标记的存活对象做转移,这个期间存在一个问题,用户线程也在运行如果在运行期间如要用到B对象而B对象的地址还是老地址没有重定位这个时候该怎么办,这个时候就出现了一个叫转发表的东西里面记录B的老指针和新指针
在这里插入图片描述

8.通过一轮GC后A对象的状态变成了Remapped (蓝色),而B还是红色,在下次GC时并发标记阶段是会判断现在对象指针的颜色是不是红色,如果是红色那么就知道该对象是还没有重定位的对象,然后就会对对象进行重定位和删除转发表中的记录删除掉,这一步操作是原子操作,是读屏障来实现的。
在这里插入图片描述
在这里插入图片描述

安全点与安全区域

如GC等,所以GC不是想什么时候做就立即触发的,是需要等待所有线程运行到安全点后才能触发。 这些特定的安全点位置主要有以下几种:

  1. 方法返回之前
  2. 调用某个方法之后
  3. 抛出异常的位置
  4. 循环的末尾 大体实现思想是当垃圾收集需要中断线程的时候, 不直接对线程操作, 仅仅简单地设置一个标志位, 各个线程执行过程 时会不停地主动去轮询这个标志, 一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。 轮询标志的地方和 安全点是重合的。
    安全区域又是什么? Safe Point 是对正在执行的线程设定的。 如果一个线程处于 Sleep 或中断状态,它就不能响应 JVM 的中断请求,再运行到 Safe Point 上。 因此 JVM 引入了 Safe Region。 Safe Region 是指在一段代码片段中,引用关系不会发生变化。在这个区域内的任意地方开始 GC 都是安全的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值