GC演变过程、三色标记法、大白话讲解G1

文章介绍了Java中垃圾收集器的发展,从串行、并行到并发GC,重点讨论了并发垃圾回收的挑战,如三色标记法和CMS、G1收集器的解决方案。CMS在解决STW问题时可能出现长时间暂停,而G1引入了分区和逻辑分代的概念,提高回收效率。G1适合大型内存应用,但需要注意其对内存分配的自动化管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GC演变过程

在Java中,垃圾收集一直是一个非常重要的组成部分, 到目前为止,垃圾收集器已经有十种了, 在不停的优化.

在这里插入图片描述

那为什么需要不停的优化呢? 其实根本在于Java程序使用的内存在不断的增加, 在最开始的时候就只有几兆,几十兆, 使用串行垃圾回收器不会浪费多多少时间, 后来慢慢发展, 到现在有的Java程序需要占用几十G的内存,那使用串行垃圾回收肯定就不适用了.

在这里插入图片描述
到目前为止, 这十种垃圾回收器其实总共可以分为三类.这里举例进行说明.

  1. 串行垃圾回收
    你、你女朋友、你男朋友在房间里扔线团(垃圾)玩, 玩了一段时候后, 你妈妈看见房间里垃圾太多了, 说不许动(STW), 然后进来帮你们收拾垃圾.收拾完出去,你们又可以继续玩了.
  2. 并行垃圾回收
    还是你、你女朋友、你男朋友在房间里扔线团(垃圾)玩, 但是后来你们生活改善了, 换了个200平的大房子, 现在你妈妈一个人打扫就太慢了, 就和你爸爸一起帮你们打扫, 当然打扫的时候,你们还是不能动.
  3. 并发垃圾回收
    每次你爸妈帮你们收拾垃圾的时候你们都不能动, 这个让你们很不爽, 感觉玩的不高兴, 于是和你爸妈反馈, 你爸妈为了满足你, 给你们雇了一个保姆, 就一直在你们房间, 一旦你们产生垃圾, 就帮你们进行处理, 就这样你们可以一边玩, 还有人帮你们收拾, 就一直可以玩了.

看到这几个样例,相信能很容易理解这几类的区别了.其实垃圾回收还有一个一直在优化的点. 就是STW(stop the word),对于web应用, 是非常关注这一点的, 因为如果在外部请求到来, 你的程序正好在垃圾回收, 花费了好几秒, 那就很影响客户体验了, 并发垃圾收集器就是为了解决这一点, CMS和G1都属于并发垃圾收集器.

在这里插入图片描述

并发垃圾回收需要解决的问题

怎么确定一个垃圾?

我们怎么确定一个对象是不是垃圾呢? 其实就是可达性分析.

通常通过建立根对象的集合,然后检查从这些根对象开始的可触及性来实现,如果正在运行的程序可以通过根对象访问到某个对象,那么这个对象就是可触及的,而无法被触及的对象被认为是垃圾.

跟对象的集合通常通过包括下面几个部分:

  1. 虚拟机栈中的局部变量的对象引用
  2. 传入本地方法栈中,没有被释放的对象引用.
  3. 方法区中的常量引用的对象.
  4. 方法区中的静态变量引用的对象.

具体实现就是从根节点开始有一个对象引用图,在追踪的过程中以某种方式打上标记,当追踪结束时,为被标记的对象就是无法触及的.

对应到我们生活中举的例子,我们往房间里扔线团, 那有线的就是正常的, 没线的就是垃圾.

并发收集存在的问题

在业务线程不断的产生垃圾的同时, 垃圾回收线程会不断的进行标识, 标识哪个对象是有用的

那我们知道线程是靠CPU调度的, 一个线程不可能一直执行, 当垃圾回收线程被挂起的时候, 已经标识好的对象的状态是有可能被改变的.遍历结束后,所有未被标记的对象都是垃圾对象,可以被回收。

比如一个对象被标识成垃圾, 然后垃圾回收线程被挂起了, 在被挂起的时候, 这个对象又重新被业务线程引用了, 又不是垃圾了, 哪这种怎么处理?

这时候就出现了一种算法, 叫做三色标记法.

三色标记法

三色标记法(Tri-color marking)是一种用于垃圾回收的算法。

它的主要思想是将所有对象分为三种颜色,所有的对象开始的时候都是白色,从程序的根集(如全局变量或寄存器)开始,逐步遍历所有与之相关联的对象,并将它们标记为灰色或黑色。

三种颜色的区别是:

  • 白色: 还没有被访问到

  • 灰色: 被访问到还没有完成, 比如说当前节点被访问是有引用, 但是它的孩子节点(成员变量)还没有被访问, 或者说该节点有三个孩子,其中有两个孩子被访问完成, 另一个还没有被访问到,也是灰色.

  • 黑色: 被访问且完成, 当前节点和他所有的孩子节点都被访问到(不包括孙子).

在这里插入图片描述

之前我们说在标识的过程中, 对象的状态可能会发生变化, 接下来我们就来分析一下, 其实总结来说, 可能会发生三种变化, 还是以A、B、D这三个对象来说.

  • 情况一: B->D消失.
    在这里插入图片描述
    这种情况其实没什么影响, 如果是垃圾引用消失就刚好被删除, 如果不是垃圾, 还有别的对象需要该引用, 那必然通过别的对象能够找到, 也不会被删除.

  • 情况二.A-D增加.
    在这里插入图片描述
    这种情况也没有影响, 因为B还是灰色, 还会扫描, 也能扫描到D.

  • 情况三: B-D消失, A->D增加
    在这里插入图片描述
    这种就会有影响了, 因为A对象是黑色的, 不会再扫描, B到D的引用又消失了, 那此时D就会被当做垃圾, 但是它不是, 明明还有对象A引用到它.

那这个问题怎么解决呢?

CMS解决方案:Incremental Update Reference
简单一句话就是当黑色对象指向白色对象, 将黑色对象标灰
那怎么知道黑色对象值向白色对象了呢?通过写屏障, 可以理解为一个切面操作,类似Spring里的AOP这些, 监测所有的引用变化,发现是黑色指向白色,那就把它变成灰色.

G1针对此问题的解决方案:SATB
简单来说就是做一个快照,还是监测所有的引用变化, 当发现灰色到白色的线消失时, 把这个引用存到专门的GC堆栈中.
然后回收线程需要对这个GC堆栈中的引用做特殊处理,就是看看有没有其他的对象需要这个引用,没有的话就是垃圾了.
在这里插入图片描述

CMS垃圾收集器

CMS是为了解决STW的第一次尝试. 也是使用并发垃圾收集的第一款垃圾收集器.

虽然CMS为了解决STW产生,但是很多长时间的STW却是由于CMS诞生的,为什么呢?

因为CMS中的垃圾回收是在某一阀值的时候开始回收,回收阀值可用指定的参数进行配置,-XX:CMSInitiatingOccupancyFraction来指定,默认为68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC的停顿时间可能较长,所以-XX:CMSInitiatingOccupancyFraction的设置要根据实际的情况.

G1垃圾收集器

介绍,主要特点

终于迎来了今天的主角, G1,目前G1已经在很多生产环境中使用了, 可以大胆的学习使用.

在之前的垃圾收集器其实都是分代的,就是有明确的新生代, 老年代, 不同的垃圾收集器负责不同的代.

而G1最关键的点,也是和之前最大的不同:
物理上是分区的,逻辑上是分代的

怎么理解?就是它实际会把内存分为一块一块的,也就是物理上分区, 但是每一块都可以动态的作为Eden区,Survivior,Old区.这个视实际情况而定.

比如,某个项目新对象很多,且产生特别快, 那就把Eden区分的多一些
比如说有的项目,老年代对象多, 且需要持续使用, 不够用, 那就把Old区分的多一些.

在这里插入图片描述

这样还有一个好处, 就是一小块一小块进行处理,比整体处理效率更高.
比如某一小块区域快满了,就把它清掉, 不需要等整体满了再去清.

优点

在这里插入图片描述

使用注意点

  1. 不建议手工指定新老年代比例.当指定之后,G1的自动调优功能就完成不了了.

在这里插入图片描述

  1. 一般使用内存大于等于8g才建议使用G1.
    因为它需要维护大量的数据结构来追踪每个区域的垃圾回收情况。这些数据结构需要占用额外的内存空间,对于小型应用程序来说可能会造成内存不足的问题。

今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bling

义父,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值