垃圾收集算法(GC)

96 阅读3分钟

垃圾算法类型

image.png

1. 标记-清除 算法

算法思想:
算法两个阶段:
  1. 标记阶段:标记出所有需要回收的对象
  2. 清除阶段:统一回收所有被标记的对象

     标记阶段:(先进行可达性分析)
        1. 第一次标记 & 筛选
           对象在可达性分析后就会被第一次标记 & 筛选
           a.不筛选 = 继续留在“即将回收”的集合里
           b.筛选 = 从 “即将回收”的集合取出
           c.筛选的标准:该对象是否有必要执行`finalize()`方法(有必要需人为设置,否则判断该对象死亡)

    
       2. 第二次标记 & 筛选

          该对象会被放到一个 F-Queue 队列中,并由 虚拟机自动建立、优先级低的Finalizer 线程去执行 队列中该对象的finalize()

          finalize()只会被执行一次
    但并不承诺等待finalize()运行结束。这是为了防止 finalize()执行缓慢 / 停止 使得 F-Queue队列其他对象永久等待。

           在执行finalize()过程中,若对象依然没与引用链上的GC Roots 直接关联 或 间接关联(即关联上与GC Roots 关联的对象),那么该对象将被判断死亡,不筛选(留在”即将回收“集合里) 并 等待回收


优点:
- 算法简单 实现简单

缺点:
-  标记&删除效率低
-  用的同一块内存进行存储,当发生GC时,容易无连续的内存,供使用

适用场景:对象存活高 & GC几率低(如老年代区域)

2.复制算法

该算法的出现是为了解决 标记-清除算法中 效率 & 空间问题的。

算法思想:将内存分为了大小相等的两块,每次只使用其中一块, 当使用的这块内存用完,就将这块内存存活对象 复制到另一块“未使用”的内存上,最终将使用的那块内存清理掉。     
缺点:内存被分成了两块,内存大小变成了一半,当对象存活率较高的情况需要做很多的复制操作。

3.标记-整理法

 此类算法类似于 "标记-清除"。只是在中间多加了一步:整理内存
 算法思路:
 a.标记阶段:标记出所有需要回收的对象
 b.整理阶段:让所有存活的对象向一端进行移动
 c.清除阶段:统一清除端以外的对象

4.分代收集法

   主流的虚拟机都使用这种算法
   
   根据对象存活周期的不同,将Java堆内存,分为:新生代 & 老生代。
   新生代:采用复制算法(这里面的存活率都不高,容易发生垃圾收集行为。Mionr GC) 
   老年代:标记-清除算法,标记-整理算法(Full GC || Major GC)
   

image.png 新生代内存分配比例:8:1:1

   具体存储的过程:
   1.新建的对象一般会优先分配到 Eden区,From Survivor区(大对象,如长的字符串和数组就会直接分配到老年代,避免发生大量的内存复制)
   2.这些对象经过第一个Minor GC如果还存活,就会移动到 To Survivor区,一次清理掉Eden,From Survivior区
   3.在To Survivior区每经历一轮Minor GC,该对象的年龄就加1
   4.当对象的年龄达到一定时(阀值默认=15),就会移动到老年代,
   
   注意:`From Survivor` 和 `To Survivor`之间会经常互换角色。
   每次发生GC时,把Eden区和 From Survivor区中 存活且没超过年龄阈值的对象 复制到To Survivor区中(此时To Survivor变成了From Survivor),然后From Survivor清空(此时From Survivor变成了To Survivor)
   
   优点:效率高,不同的区域采用不同的GC算法