垃圾回收的常见算法

11 篇文章 0 订阅

标记阶段:引用计数法(不采用)

原理

假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器

就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。

优缺点

优点:

实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。

在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。

区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。

缺点:

每次对象被引用时,都需要去更新计数器,有一点时间开销。

浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。

无法解决循环引用问题。(最大的缺点)

标记阶段:可达性分析

以根对象集合(GC Roots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达;使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜素所走过的路径称为引用链;如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象;在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象

如果要使用可达性分析算法来判断内存是否可回收,那么分析工作必须在一个能保障一致性的快照中进行,所以GC进行时必须“Stop The World”

GC Roots包括:

虚拟机栈中引用的对象:各个线程被调用的方法中使用到的参数、局部变量等

本地方法栈内引用的对象:

方法区中类静态属性引用的对象:Java类的引用类型静态变量

方法区中常量引用的对象:字符串常量池里的引用

所有被同步锁synchronized持有的对象

Java虚拟机内部的引用:基本数据类型对应的Class对象,一些常驻的异常对象,系统类加载器

三色标记算法思想

三色标记法将对象的颜色分为了黑、灰、白,三种颜色。

白色:该对象没有被标记过。(对象垃圾)

灰色:该对象已经被标记过了,但该对象下的属性没有全被标记完。(GC需要从此对象中去寻找垃圾)

黑色:该对象已经被标记过了,且该对象下的属性也全部都被标记过了。(程序所需要的对象)

算法流程

从我们main方法的根对象(JVM中称为GC Root)开始沿着他们的对象向下查找,用黑灰白的规则,标记出所有跟GC Root相连接的对象,扫描一遍结束后,一般需要进行一次短暂的STW(Stop The World),再次进行扫描,此时因为黑色对象的属性都也已经被标记过了,所以只需找出灰色对象并顺着继续往下标记(且因为大部分的标记工作已经在第一次并发的时候发生了,所以灰色对象数量会很少,标记时间也会短很多), 此时程序继续执行,GC线程扫描所有的内存,找出扫描之后依旧被标记为白色的对象(垃圾),清除。

具体流程

首先创建三个集合:白、灰、黑。

将所有对象放入白色集合中。

然后从根节点开始遍历所有对象(注意这里并不递归遍历),把遍历到的对象从白色集合放入灰色集合。

之后遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合

重复 4 直到灰色中无任何对象

通过write-barrier检测对象有变化,重复以上操作

收集所有白色对象(垃圾)

三色标记存在问题

浮动垃圾:并发标记的过程中,若一个已经被标记成黑色或者灰色的对象,突然变成了垃圾,由于不会再对黑色标记过的对象重新扫描,所以不会被发现,那么这个对象不是白色的但是不会被清除,重新标记也不能从GC Root中去找到,所以成为了浮动垃圾,浮动垃圾对系统的影响不大,留给下一次GC进行处理即可。

对象漏标问题(需要的对象被回收):并发标记的过程中,一个业务线程将一个未被扫描过的白色对象断开引用成为垃圾(删除引用),同时黑色对象引用了该对象(增加引用)(这两部可以不分先后顺序);因为黑色对象的含义为其属性都已经被标记过了,重新标记也不会从黑色对象中去找,导致该对象被程序所需要,却又要被GC回收,此问题会导致系统出现问题,而CMS与G1,两种回收器在使用三色标记法时,都采取了一些措施来应对这些问题,CMS对增加引用环节进行处理(Increment Update),G1则对删除引用环节进行处理(SATB)。

清除阶段:标记清除法(年老代)

标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。

标记:从根节点开始标记引用的对象。

清除:未被标记引用的对象就是垃圾对象,可以被清理。

原理

程序运行期间所有对象的状态,它们的标志位全部是0(也就是未标记),假设这会儿有效内存空间耗尽了,JVM将会停止应用程序的运行并开启GC线程,然后开始进行标记工作,按照根搜索算法,标记完以后,按照根搜索算法,所有从root对象可达的对象就被标记为了存活的对象,此时已经完成了第一阶段标记。接下来,就要执行第二阶段清除了,没有被标记的对象将会回收清除掉,而被标记的对象将会留下,并且会将标记位重新归0。接下来唤醒停止的程序线程,让程序继续运行即可。

优缺点

优点:

标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。

缺点:

效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。

通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

为对象分配内存时,内存不规整,需要维护一个空闲列表

清除阶段:标记压缩算法(年老代)

标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。

优缺点

优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响

为对象分配内存时,内存规整,使用指针碰撞(如果内存空间以规整和有序的方式分布,即已用和未用的内存都各自一边,彼此之间维系着一个记录下一次分配起始点的标记指针,当为新对象分配内存时,只需要通过修改指针的偏移量将新对象分配在第一个空闲内存位置上)

清除阶段:复制算法(年轻代)

复制算法的核心就是,将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该方式并且效率比较高,反之,则不适合。

优缺点

优点:

在垃圾对象多的情况下,效率较高

清理后,内存无碎片

缺点:

在垃圾对象少的情况下,不适用,如:老年代内存

分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

分代算法

每一种回收算法都有自己的优点也有缺点,谁都不能替代谁,分代算法根据回收对象的特点选择回收算法,不同生命周期的对象可以采用不同的收集方式,以便提高回收效率,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法。

 增量收集算法

让垃圾收集线程和应用线程交替执行,每次垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程,依次反复,直到垃圾收集完成,增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作

缺点:使用这种方法,在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统地停顿时间,但是,因为线程切换上下文转换地消耗,会使得垃圾回收地总体成本上升,造成系统吞吐量的下降

分区算法

分区算法将整个堆划分成连续的不同小区间,每一个小区间都独立使用,独立回收,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生地停顿。这种算法大的好处是可以控制一次回收多个小区间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值