转载

垃圾对象判断与回收

顾名思义,这个方法其实就是判断对象的引用数量来判断这个对象是否是垃圾对象.给对象添加引用计数器,每多一个引用则引用数量+1,每少一个引用则引用数量-1.当此对象引用数量为零,则对象为垃圾对象.这种方式实现简单且高效,但是这种方式存在着一种问题,也就是对象的循环依赖.假设现在有两个对象,他们互相引用值为null,且不存在其他对象对其引用也不存在这两个对象的使用,那么这时这两个对象应该就是垃圾对象,但是由于他们互相引用,所以无法被回收.

可达性分析算法

我们还是根据名字推测他的意思,可达性即这个对象可以到达,对可达性进行分析来判断对象是否存在引用,如果对象可达则不能回收,如果不可达则将对象标记为垃圾对象等待回收,这也是主流虚拟机采用的方式,那么这个可达性是什么呢,要了解这个概念,我们首先需要了解GC Roots根节点.

GC Roots根

我们都知道java是单根结构,可以根据一个Object根发展出一颗依赖树来.那么这个GC Roots根节点其实也有着异曲同工之妙.他同样的可以以GC Root作为根节点向下发展,发展出一颗依赖树,通过这个GC Root向下探寻,可以探寻到的对象我们就称其为可达的.值得注意的一点是GC Root可以有多个.

垃圾对象判断与回收

GC Roots根是什么

GC Roots有很多种类型,常见的比如说栈帧里的本地变量(指向堆内存的对象),静态变量等等

无用类

无用类(并非学名)需要符合以下几个条件

  • 这个类的所有引用已经消失(被回收)
  • 这个类的class信息被回收(不能通过反射创建此对象的实例)
  • 这个类的类加载器被回收

垃圾对象标记

垃圾对象的标记其实是分为两个阶段的,主要是看这个对象是否重写了Object类的finalize()方法.

通过可达性分析我们可以对垃圾对象进行标记.但是对象其实是有一个二次标记的过程的,重写finalize()方法可以进行第二次标记,如果对象没有重写这个方法则直接视为垃圾对象.这一些执行第二次标记的对象,会执行这个方法内的代码,如果在这个方法中把本来应该是垃圾对象的对象重新引用,比如设置一个成员变量并将这个实例赋给这个变量.那么这个对象就不会被视为垃圾对象,如果没有对其进行重新引用则此对象视为垃圾对象.

垃圾回收算法

垃圾回收算法主要分为四种

标记-清除算法

这个算法分为两个部分,"标记","清除".

标记即上述内容,通过可达性分析算法,不可达对象标记,二次标记中没有自救的对象.

清除就是进行消除工作

垃圾对象判断与回收

这个算法具有两个问题,效率问题与空间问题.

  • 效率问题:由于垃圾对象存在不连续又没有引用,所以标记的过程很耗时
  • 空间问题:这种清除算法会造成一些微小的内存碎片,从而造成内存的损失.

内存碎片就是占用空间小没法放下其他对象的的小内存空间.

标记-复制算法

垃圾对象判断与回收

这个算法将一块内存区域分为完全相等的两块,这两块只能使用一块,另一块做什么用呢,用于垃圾清除后的空间整理.进行垃圾清除时,他会把当前这一半的所有存活对象依次移入到另一半内存中去,移入完成后在对这一块内存进行整体回收.由于每次清理对象实际都存在一个整理的过程,所以这种算法不会产生垃圾碎片问题,又因为他是保留有用的对象,直接全部回收一块内存空间,速度自然也比标记-清除算法快很多.

标记-整理算法

标记过程都相同,整理首先是将存活对象向前移动,将所有存活对象移到前面去,那么此时会产生一个内存分界点,分界点一端全部都是有用对象,另一端全部都是垃圾对象,这时候只需要对分界点的一端进行整体清理就可以了.这个算法同样不会产生垃圾碎片,效率也不低.

垃圾对象判断与回收

分代收集算法

分代收集算法并不是像前三种算法那样做一个清除的步骤,他只不过是将堆内存分为了年轻代老年代,这样我们就可以通过不同的策略针对不同的区采取不同的垃圾收集算法.

原文  https://juejin.im/post/5d920f8251882558b77d288c
正文到此结束
Loading...