垃圾回收算法

垃圾回收算法

2020-09-27
java

垃圾标记算法 #

  • 引用计数器法

    • 给一个对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
    • 优点:实现简单,垃圾对象便于识别;判定效率高,回收没有延迟性。
    • 缺点:需要单独的字段存储计数器,增加了存储空间的开销;每次赋值都要更新计数器,伴随着加减法的操作,增加了时间开销;引用计数器有一个严重的问题,即无法处理循环引用情况。这是一个致命缺陷,导致java在垃圾回收器中没有使用这类算法。
  • 可达性分析算法

    • 基本思路:以根对象集合(GC Roots)为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
    • GC Roots包括:①虚拟机栈中引用的对象;②类静态属性引用的对象;③方法区中常量引用的对象;④本地方法栈中(Native方法)引用的对象;⑤被synchronized持有的对象;⑥jvm内部的引用。

finalize #

  • 垃圾回收之前总会调用finalize方法,该方法可以被重写:通常是在这个方法中进行一些资源释放和清理的工作。

  • 不要主动调用该方法,该方法的执行时间是没有保障的,它完全由gc线程决定。垃圾回收机制会主动调用该方法。

  • finalize方法只能被调用一次。

  • 对象的三种状态:可触及;可复活;不可触及。

  • 判断一个对象是否可回收,至少经历两次标记过程:

    1. 如果对象到gc roots没有引用链,则进行第一次标记
    2. 进行筛选,判断该对象是否有必要执行finalize方法:①如果对象没有重写finalize方法,或者finalize方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,该对象被判为不可及的;②如果对象重写了finalize方法且还未执行过,那么该对象将被插入到F-Queue队列中,由一个低优先级、虚拟机自动创建的Finalizer线程区执行它;③finalize方法是对象逃脱死亡命运的最后一次机会,稍后GC会对F-Queue中的对象进行第二次标记,如果对象在finalize方法中重新与引用链上的任意一个对象建立了联系,那么在第二次标记时它将被移出“即将回收”的集合。之后,如果该对象再次出现没有引用存在的情况下,finalize方法不会再次调用,对象会直接变为不可及的状态。也就是说一个对象的finalize方法就被调用一次。

垃圾收集算法 #

  • 标记-清除算法(Mark-Sweep)

    • 执行过程:当堆中的有效内存空间被耗尽的时候,就会停止整个程序(STW),然后进行两项工作,第一项是标记,第二项是回收。
    • 标记:从根节点开始,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。
    • 清除:对堆内存从头到尾进行线性的遍历,如果发现某个对象在其header中没有被标记为可达对象,则将其回收。
    • 优点:实现简单。
    • 缺点:效率不够高;导致STW;会导致内存空间不连续,产生内存碎片,需要维护一个空闲列表。

标记-清除算法

  • 复制算法

    • 执行过程:将可用内存按容量分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还活着的对象复制到另外一块上面去,然后再把已使用过的内存空间一次清理掉。
    • 优点:实现简单;运行高效,不会出现内存碎片。
    • 缺点:需要两倍的内存空间。
    • 适用场景:存活对象比较少,垃圾对象比较多的场景。

复制算法

  • 标记-压缩(整理)算法
    • 执行过程:第一阶段和标记-清除算法一样,从根节点标记所有被引用的对象。第二阶段是将所有存活对象整理到内存的一端,按顺序排放。之后清理边界外的所有对象。
    • 指针碰撞:如果内存空间以规整、有序的方式分布,即已用和未用的内存都各自一边,彼此之间维系着一个记录下一次分配起始点的标记指针,当为新对象分配内存时,只需要通过修改指针的偏移量将新对象分配在第一个空闲内存位置上,这种分配方式就叫指针碰撞(Bump the Pointer)。
    • 优点:解决了内存碎片化的问题;消除了复制算法中内存减半的问题。
    • 缺点:效率低于复制算法和标记-清除算法;移动对象的同时,还要调整引用的地址;会导致stw。

标记-整理算法

  • 分代收集算法

    • 不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采用不同的收集方式,以提高回收效率。一般把java堆分为新生代和老年代,这样就可以根据各个年代的特点使用不同的回收算法,以提高垃圾回收的效率。
  • 增量收集算法

    • 如果一次将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。每次,垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程,依次反复,直到垃圾收集完成。
    • 总的来说,增量收集算法的基础仍然是传统的标记-清除算法和复制算法。增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集器以分阶段的方式完成标记、清理和复制工作。
    • 缺点:线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量下降。
  • 分区算法

    • 将堆空间划分为连续不同的小区间region,每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。