JVM垃圾回收策略GC

举报
bug郭 发表于 2022/10/06 22:17:09 2022/10/06
【摘要】 垃圾回收策略(GC)我们知道内存的申请是由我们程序员控制的,当我们创建了一个变量,就是申请了一块内存空间,而当我们这个变量不用时,应该将这块空间释放!这里释放归还的过程就会用到垃圾回收策略garbage collection简称GC!而有的编程语言像C/C++内存的释放还是通过程序员自己释放,但是这样通过程序员自己释放就会降低开发效率!我们程序员有时候很难控制释放的时间,如果释放的早,就会...

垃圾回收策略(GC)

我们知道内存的申请是由我们程序员控制的,当我们创建了一个变量,就是申请了一块内存空间,而当我们这个变量不用时,应该将这块空间释放!这里释放归还的过程就会用到垃圾回收策略garbage collection简称GC!
而有的编程语言像C/C++内存的释放还是通过程序员自己释放,但是这样通过程序员自己释放就会降低开发效率!我们程序员有时候很难控制释放的时间,如果释放的早,就会导致申请释放内存的开销,如果释放的晚,或者没有进行释放,就会导致内存泄漏问题!
大部分主流语言还是通过一个专门的进程,对内存空间进行释放,也就是我们这里说的垃圾回收!像Python/java/go/PHP等都是如此,不过不同语言的垃圾回收策略会有不同,我们主要学习java中的JVM是如何进行GC的!
垃圾回收策略的缺点:
1).消化额外的开销(我们在JVM下搞一个需要一个专门进程,这样消化的资源就多了)
2).可能影响程序运行的流畅度(垃圾回收会导致STW(Stop The World)问题,就是程序中断,注意这里说的中断并不是我们多线程学的中断,这里指的是GC要对垃圾进行回收,使得我们的业务程序不得不停止!)

我们知道了内存区域是如何划分的,而我们划分的空间如果不用了,就需要回收!
我们JVM的内存空间是想操作系统申请的!当我们不使用时,就需要将其归还,有借有还再借不难!
我们的JVM是如何判断一块空间是不用的,又是如何进行回收的呢?

垃圾回收都回收啥内存?

我们将JVM的内存空间进行了划分,GC主要回收的内存空间就是堆上的空间,因为这块空间最大,也是我们需要回收的空间,不像栈会根据程序的执行,自己释放!程序计数器的空间大小是固定了也不需要释放,方法区存放的是类对象,而类对象只在类加载时加载一次创建一次,最后该类结束后,进行类卸载,需要释放内存,这里是比较低频的操作!我们GC的关键就是针对堆上的空间进行回收,因为我们代码中大量的内存空间都是在堆上的!

在这里插入图片描述
上图就是大致我们堆上的内存的使用分布!
我们可以知道我们这里的CG基本单位是对象,并不是字节,就好比对象1,有些变量还在使用,有一些不在使用,我们就要保留这块内存空间,自制所有的内存都不在使用,就将其回收!我们主要针对一整个对象回收!

如何定位垃圾?
当下垃圾回收机制有2个主流的定位垃圾的机制

  • 基于引用计数
  • 基于可达性分析

基于引用计数

我们堆上主要的保存的就是我们new的对象和成员变量,我们可以根据一块空间记录指向一个对象的引用个数,然后根据记录的引用数决定是否需要将这块空间回收,如果引用为0说明这个对象已不再使用,就可以进行垃圾回收,这就是引用计数的方式!

t1 = new A();//new A()对象的引用加一!
t2 = new B();//new B()对象的引用加一!

在这里插入图片描述
我们的A对象只有有t1引用指向,所以引用数为1,B对象也是如此!
当某一时刻,该引用变量释放,对应的对象引用计数也会减1,然后为0时就会将这块内存空间视为垃圾,将其回收!
循环引用问题:

t1 = new A();
t1.t = new B();
t2 = new B();
t2.t = new A();

在这里插入图片描述
循环引用就是某一引用变量t1下面的属性也有引用t指向一个对象,当外面的引用变量t1释放后,new A()对象的引用计数是减1了,但是new B()对象的引用计数不会减少,t2也是如此,最后虽然t1t2引用已经不存在,但是对象A和对象B的引用数还是1,这就尴尬了,虽然没人能拿到这两个对象,但是引用计数还是1,不能释放…

引用计数的缺点

  • 要消耗额外空间,我们需要一块特点的空间记录引用数!当我们的对象本身空间就不大时,引用计数空间就很费资源!
  • 循环引用问题,不能进准定位到垃圾!

基于可达性分析

我们上述的引用计数是其他语言(PHP/Python)使用的定位垃圾的手段,而我们的java使用的定位垃圾的策略是可达性分析!

可达性分析: 就是通过额外的线程,对内存进行扫描,然后可以扫描到的对象就将其标记,这里就需要规定扫描的起始位置GCRoots,通过起始位置,类似于二叉树的深度优先遍历一样,将能够访问到的对象都标记一遍,访问不到的就是是不在使用的内存空间,也就是垃圾了!

GCRoots:

1).栈上的局部变量!
2).常量池中引用指向的对象
3).方法区中静态变量指向的对象

我们用二叉树遍历来模拟GC可达性分析:
在这里插入图片描述
我们通过GCRoots起始位置对内存空间进行深度优先扫描,我们可以扫描到的节点对象就是真正使用内存的对象(a,b,d,e,f,g),我们将其标记,而没有扫描到的对象(h,j)也就是垃圾,我们将其空间回收即可!

我们通过引用计数和可达性分析可以将垃圾定位,那应该如何对垃圾进行处理呢也就是回收内存?
我们通过3种算法机制对垃圾进行回收:

  • 标记清楚
  • 复制算法
  • 标记整理

标记清除

标记清楚就是对垃圾进行标记,然后将该标记的空间进行清楚回收即可!

在这里插入图片描述
可以看到这种清除垃圾的算法,将内存空间回收后,会造成大量内存碎片,造成空间浪费!

复制算法

复制算法就是空间分成2分,一份用于对象使用,一份用于复制!
当标记到了垃圾,我们需要对这一半空间中的对象复制到另外一半空间,然后整体回收这一半的空间!

在这里插入图片描述
虽然这个算法解决了标记清楚的内存碎片问题,但是空间利用率低!有一半的空间未被使用到!

标记整理

标记整理,就类似数组中删除 某一元素,需要将数组元素进行整理!

在这里插入图片描述
显然这种算法解决了上述2个算法的缺点,但是效率比较低,每次整理都要耗费大量时间开销!

我们GC在进行垃圾回收时会根据不同的情况,使用不同的算法进行回收垃圾!

分代回收

我们JVM下的垃圾回收,将多种方案进行了结合, 一起使用,叫做分代回收!
这里的分代是根据"年龄"进行划分的,这里的年龄并不是传统意义上的年龄,是指经过一轮GC扫描后,如果对象还在,那这个对象就长一岁!根据不同岁数的对象进行了划分!

在这里插入图片描述
我们将堆空间进行分区,首先2个大类,新生代和老年代,然后新生代下又进行了划分,伊甸区和2个幸存区!

  • 新生代

    • 伊甸区

    我们的创建好的对象直接放入到新生代中的伊甸区下!并且伊甸区大部分对象经过一轮GC扫描后就会进行回收,只有少部分还存在!

    • 幸存区

    当伊甸区经过一轮GC扫描后,幸存的对象,就来到辛存区!辛存区的对象也会经过多轮GC扫描,这里的垃圾回收算法采用的是复制算法!然后经过多轮的GC扫描后没有被淘汰的对象就来到了老年代!

  • 老年代

老年代下的对象,GC扫描的频率会大大减低,并且这里垃圾回收的算法采用的是标记整理算法!这里的GC扫描会经过很长的时间进行扫描,因为一般能来到老年代下的对象,命都比较硬!
注意:这里老年代下的对象除了是辛存区下来的,还有就是所占空间比较大的对象,直接就来到老年代,因为大对象,不适合复制算法进行回收,并且大对象一般存活的时间也比较长!

我们类比我们找工作的过程来理解分代回收机制:

首先投简历直接来到伊甸区,啪的一下,面试官进行一轮简历筛选,大部分简历直接就丢了,然后剩下的人就来到了辛存区,好比我们通过简历后要经过很多轮的笔试和面试,最后才能拿到offer,拿到offer后,我们就来到了老年代,虽然已经进公司了,但也不是就稳定了,如果干的不好,就会将其淘汰!而这里有一些牛逼大大佬,直接就免了笔试和面试,直接就进公司了,就好比大对象一样!

垃圾收集器

我们上述介绍的回收机制只是思想,如果要具体落地实现,要通过JVM中的垃圾收集器具体进行回收,因为随着JVM版本的更迭,收集器也不断的更新!所以我们就大致了解一下即可!
在这里插入图片描述

  • 串行收集

Serial 针对新生代
Serial Old针对老年代
在进行垃圾回收时,我们的业务线程需要停止工作,这种方式扫描的慢释放的页慢,产生了严重的STW!

  • 并发收集

ParNew
Parallel Scavenge上面2个都是针对新生代
Parallel Old:针对老年代
并发收集,引入了多线程,但是也是比较低效的收集方式!

  • CMS收集器

设计比较巧妙,尽可能减少STW

  • 可达性分析
    1).初始标记,速度很快,会引起短时间的STW,这里的标记只是为了找到Roots
    2).并发标记,比较慢,但是这是和业务线程并发的,不会产生STW
    3).重新标记,在并发标记时,并发的业务代码可能会影响标记结果,所以对标记进行微调,速度较快,会引起短时间的STW
  • 标记整理
    4).回收内存,和业务线程并发!

-G1收集器

把整个内存分成很多个小的区域Region,给这些Region进行标记,然后根据年龄放入不同的分代区域,扫描的时候一次扫描若干个Region,分多次扫描,所以影响业务代码最小,可以使STW减小到1ms

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。