gc回收流程

摘要:

  垃圾回收这一步对于jvm十分重要,内存管理虽然不用手动管理,但是对于查错确实,排错确是十分重要哦!

大致流程:允许GC之后(注意,发动GC也是需要一定的要求步骤,不详细展开,会另写博客进行说明)-> 开始查找那些允许被回收的(两个算法)-> 开始回收(四个算法)

上述是简单的流程,根据不同的垃圾回收器,用到的思想大致如此,具体实现步骤不同。

有请提示:垃圾回收器是可以用到好几个算法,组合起来更强大。然后jvm进行GC的时候,也是将垃圾回收器进行组合的。

第一步:那些对象是垃圾:

     1,引用计数法:通过对引用的遍历,找到对应的实例,让对应的实例计数加  1 ,如果引用取消,或者指向null,实例的引用减  1 。把找到的引用都遍历一遍之后,如果发现有对象实例的计数是0。那么这个对象 就是垃圾对象了。在通过垃圾回收算法对其进行 回收即可。

   缺点:想想一下,有两个类,互相引用,也就是A对象的实例(也就是对象的全局变量)是一个指向B对象的引用,B对象实例是一个指向A对象的引用。那么这两个对象的引用计数,永远不可能是0 。也就不可能对其进行回收了。

    2,可达性分析法:这个算法类似于树的遍历,学过数据结构的小伙伴应该会好理解。简单来说,按照一定的规则说明那些可以作为一个根节点(GC root),然后以这些根节点去访问其引用的对象,被访问的对象又会有其他对象的引用。想象一下,是不是像极了树的遍历。这个路径称作引用链,但凡是在引用链上的对象,都是可用的。注意,引用连的起始点都是GC root 哦。虽然有其他对象存在类似于引用链的结构,但是,起始点不是GC root的那一些,都是垃圾,可以被回收的。

     GC root的查找规则:java栈中的引用,方法区中的静态属性(静态变量 + 静态常量),方法区中常量引用的对象(方法区中有个结构 叫做 常量池 ,存储的一部分是常量),本地方法(线程独占区中有个结构叫做 本地方法栈)。

一般情况下,都是使用的 可达性分析法去查找垃圾类实例。

第二步:垃圾回收器算法(标记-清除、复制算法、标记-整理、分代算法)

   1,标记-清除:找到垃圾类之后,标记一下。然后直接 清除即可。(算法很快)

       缺点:产生空间碎片,不利于大对象的安排进去。

   2,复制算法:将内存分为四块:新生代(Eden),生存代(Survivor * 2),老年代。有五种内存分配策略,讲完之后再说。类的升级流程是Eden->Survivor->老年代;

     算法流程:1),先找到垃圾类,将可以使用的类移动到Survivor2,将Eden + 另一块Survivor1中的内存全部清除。

                       2),将新生成的类实例优先分配到Eden,分配不下时,放到Survivor2。进行GC时,将Survivor2中对象的满足一定条件(例如对象年龄达到某一个标准)的对象分配到老年代中。将本次GC存活下来的分配到Survivor1中,在清除Eden + Survivor2 。依次循环即可。

   缺点:很容易发现吧,Survivor中每次都会浪费一个Survivor的内存没有使用,所以为了减少浪费,一半将Eden的内存扩大,Survivor的内存设置小一点。例如:HotSpot(HotSpot是8中的jvm默认虚拟机) 中设置的是 8 : 1 : 1;

3,标记-整理:看名字是不是感觉很熟悉,没错。跟标记-清除很像,也是直接标记。改算法使用到了前面两个算法的精华,改善了缺点。

    算法流程:1),直接标记

                      2),集中,无缝隙的移动到一端,此时会发现,剩下的垃圾类,都会在其他地方。移动完成之后就会发现有一个边界,就是可用类跟其他空间的一个边界,下一步直接把边界以外的空间直接清除掉就可以了。

   缺点:看起来很完美,但是越完美的,往往在时间上过不去。

4,分代算法:根据在哪里清除,选用算法不一样。

    算法流程:1),新生代采用复制算法

                      2),老年代采用标记-清除算法(老年代GC很少访问,类也很少去直接分配到里面,内存碎片的可怕性就显得不那么重要了)

下面说说内存分配原则:

    1,对象优先分配到Eden区域;

    2,大对象直接分配到老年区:大对象的就是,对象里面有很大数组或者很大的字符串;

    3,长时间存活的对象存入老年区:就是上面复制算法里面说的那个对象升级流程;

     4,动态对象年龄判定:jvm并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才可以进入老年代,如果Survivor空间中年龄相同的所有对象的总空间>=本servivor中的一半,那么年龄>=本年龄的对象可以直接进入老年区;

    5,空间分配原则:简单来说,就是在发生Minor GC(在新生代进行GC)情况下,为了防止发生在Minor GC后,Eden有大量存活的对象,导致survivor不能全部存入,这时需要老年代去担保,把这些对象放入老年代,但是要确保老年要存的下。

           1),再发生Minor GC之前,检查老年区的可用的连续空间是否是大于新生代(Eden)的所有对象的总空间,如果是,直接全部晋升老年代,保证Minor GC的安全;

           2),如果不行,就检查HandlePromotionFailure(可以手工设定)参数时候允许担保失败,允许的话,直接分配。不能的话,发生一次full GC(或者是Major GC   在老年代进行GC)。

          3),不允许担保失败,发生一次 full GC。

        为什么不直接进行full GC ,因为速度慢呀。而且经常GC 也 效果不大,因为老年代都是一些长期存活的对象。

最后一个模块:垃圾回收器,主要有七种。说一下工作流程跟特点。注意哦,jvm可以根据不同的场景,使用不同的垃圾回收器。例如:如果要对老年代进行回收的话,可以选择

     收集器有:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1(G1是目前最好的收集器)。

img

上图是HotSpot的垃圾收集器的使用范围,HotSpot是现在主流的 jvm。

1,Serial收集器 跟 Serial Old收集器:

  这个两个收集器看名字就知道,Serial是针对于新生代的,Serial Old是针对老年代的。是最古老的垃圾收集器。

 工作原理:要进行垃圾回收(GC)的时候,首先会进行StopTheWorld(意思是将目前所有的线程都停止掉),Serial 采用复制算法进行 单线程 处理,Serial Old采用标记-整理算法 对老年代进行 单线程处理。

 优势:虽然是最古老的垃圾收集器,但是会简单高效,对于单cpu的,收集效率会很高,因为没有线程之间切换带来的时间消耗。可以用在Client(客户端),桌面应用上。

2,ParNew收集器:

  ParNew收集器其实就是Serial收集器的多线程版本。但是,它确实目前server模式下虚拟机首选的垃圾收集器,原因后说。

  工作原理:就是在StopTheWorld之后使用多线程进行回收垃圾。使用算法是复制算法。

  优势:为什么会是首选的垃圾回收器,最主要的原因是因为CMS,CMS是个跨时代的收集器,真正意义上实现了并发收集垃圾,也就是说,妈妈在收拾房间的时候, 你还可以丢垃圾,而Serial、ParNew、Parallel都是你的乖乖待在一个地方,直到妈妈把房间打扫好之后才可以继续制造。为什么还需要ParNew呢?因为CMS是针对老年代进行回收的,还缺一个对新生代回收的,而新生代回收的垃圾回收器有三个:Serial、ParNew、Parallel。Parallel性能肯定比ParNew性能好,为什么不使用呢?因为在JDK1.4.0中的存在的Parallel不能跟CMS一起配合使用,那能配合使用的只有Serial跟ParNew了,而现在服务器都是多核CPU,所以ParNew就成了虚拟机优先使用的垃圾收集器了。

3,Parallel收集器:

  Parallel 收集器是一个新生代收集器,注重的是收集器的吞吐量(Parallel收集器也被称作是“吞吐量优先”收集器)

  工作原理:跟ParNew收集器工作原理相似,只不过是严格控制了吞吐量(吞吐量就是CPU执行用户代码的时间比上CPU总消耗时间的比值,既(运行用户代码时间/(运行用户代码时间 + GC回收时间)))

  优势:严格控制吞吐量,用户体验会更好,因为卡顿会更少。ParNew需要自己去设置参数,在Parallel中有一个参数

-XX:+UserAdaptiveSizePolicy
开关参数,开启后,不需要手工指定新生代大小、Eden 跟 Servivor比例,晋升老年代对象大小参数的细节参数,jvm会根据系统的运行情况收集性能监控信息,动态调整这些参数,这些操作称为GC自适应调节策略。对于新生会很友好。

    这个也是Parallel跟ParNew的最主要的区别。

4,Parallel Old收集器:

  产生原因:比较有意思,因为Parallel的地位很尴尬,因为Parallel是适用于新生代,那老年代有谁呢,一个是CMS,一个是Serial Old。但是CMS不能跟Parallel配合使用,但是Serial Old(在多核CPU中)会拖后腿。于是,本收集器产生了。终于Paralle 有了自己的组合搭档(Parallel + Parallel Old)

  工作原理:多线程 + 标记-整理算法。其他流程跟Parallel、ParNew、Serial一样。

  优势:在注意吞吐量以及CPU资源敏感的场合,都可以优先考虑使用Parallel + Parallel Old组合。

5,CMS收集器

  他来了,他来了。

  CMS收集器是一种以获取最短回收停顿时间为目标的收集器。

 工作原理:基于标记-清除算法实现的,流程分为4步:

                   1),初始标记(仅仅标记一下GC Root,)

                   2),并发标记(是一个单独的线程,一起跟用户的线程一起 运行)

                   3),重新标记(去标记一下在运行期间发生变化的对象)

                   4),并发清除(是一个单独的线程,一起跟用户的线程一起 运行)

    其中,初始标记跟重新标记任然需要Stop The World。

img

图中的安全点,跟发动GC有关,任何一个GC收集器动作都会设计安全点。另附博客说明。

6,G1收集器:

  目前最强收集器,强到什么地步。不需要其他收集器配合,自己就可以管理新生代跟老年代。G1是面向服务端应用的垃圾收集器,HotSpot开发团队称在未来可以替换掉JDK1.5中发布的CMS收集器。

  工作流程:算法:整体采用了标记-整理算法,局部使用了复制算法。工作流程也是分为4步:

                    1),初始标记

                    2),并发标记

                    3),最终标记

                   4),筛选回收

跟CMS的工作流程差不多。因为G1还正在开发优化,在大数据上应用停顿时间以及吞吐量还有缺陷。还没有大方面的普及。
————————————————
版权声明:本文为CSDN博主「风吹落叶余花香」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
4),筛选回收

跟CMS的工作流程差不多。因为G1还正在开发优化,在大数据上应用停顿时间以及吞吐量还有缺陷。还没有大方面的普及。

-----面试题领取请添加qq2022128527------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值