垃圾回收相关算法

垃圾标记阶段

对象存活判断

在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段

引用计数算法

  1. 最早的也是最简单的垃圾回收实现方法,这种方法为占用物理空间的对象附加一个计数器,当有其他对象引用这个对象时计数器加一,反之引用解除时减一。这种算法会定期检查尚未被回收的对象的计数器,为零的话则回收其所占物理空间,因为此时的对象已经无法访问。这种方法无法回收循环引用的存储对象
  2. 优点:实现简单,垃圾对象便于辨识,回收没有延迟性
  3. 缺点
    3.1 它需要单独的字段存储计数器,这样的做法增加了存储空间的开销
    3.2 每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销
    3.3 无法回收循环引用的存储对象,因为这个导致Java的垃圾回收器中没有采用这个算法

可达性分析算法

  1. 通过分析某些“根”对象的引用关系,来确定需要保留的可访问对象,并释放其余的不可访问对象的内存空间
  2. 所谓"GC Roots"根集合是一组必须活跃的引用
  3. 可达性分析算法是以根对象集合(GC Roots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达
  4. 使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)
  5. 如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象
  6. 在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象
  7. GC Roots包含以下几类元素
    7.1 虚拟机栈中引用的对象
    7.2 本地方法栈内JNI引用的对象
    7.3 方法区中类静态属性引用的变量
    7.4 方法区中常量引用的对象
    7.5 所有被同步锁持有的对象
    7.6 Java虚拟机内部的引用
    7.7 反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
  8. 如果使用可达性分析算法,分析工作必须在一个能保障一致性的快照中进行,这也是进行GC会导致"Stop The World"的重要原因

垃圾清除阶段

当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存。|

标记-清除算法

  1. 先暂停整个程序的全部运行线程,让回收线程以单线程进行扫描标记,并进行直接清除回收,然后回收完成后,恢复运行线程。这样会产生大量的空闲空间碎片,使大容量对象不容易获得连续的内存空间,而造成空间浪费。
  2. 缺点
    2.1 效率较低
    2.2 在进行GC时,需要停止整个成员,用户体验差
    2.3 这样会产生大量的空闲空间碎片,需要维护一个空闲列表
  3. 这里的清除是把需要清除的对象地址保存在空闲的地址列表中,有新对象要加载时,判断垃圾的位置空间是否足够,足够就存放

复制算法

  1. 需要程序将所拥有的内存空间分成两个部分。程序运行所需的存储对象先存储在其中一个分区(定义为“分区0”)。同样暂停整个程序的全部运行线程,进行标记后,回收期间将保留的存储对象搬运汇集到另一个分区(定义为“分区1”),完成回收,程序在本次回收后将接下来产生的存储对象会存储到“分区1”。在下一次回收时,两个分区的角色对调
  2. 优点
    2.1 没有标记和清除的过程,运行高效
    2.2 不会出现"碎片"的问题
  3. 缺点
    3.1 需要2倍的空间
    3.2 对于G1这种分拆成为大量region的Gc,复制而不是移动,意味着cc需要维护region之间对象引用关系,不管是内存占用或者时间开销也不小
  4. 复制算法需要复制的存活对象数量特别低,比如新生代

标记压缩算法

  1. 和“标记-清除”相似,不同的是,回收期间同时会将保留的存储对象搬运汇集到连续的内存空间,从而整合空闲空间,避免内存碎片化
  2. 优点
    2.1 消除了标记-清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可
    2.2 消除了复制算法当中,内存减半的高额代价
  3. 缺点
    3.1 从效率上来说,标记-整理算法要低于复制算法
    3.2 移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址
    3.3 移动过程中,需要全程暂停用户应用程序。即:STW

分代收集算法

依照对象存活时间的长短使用不同的垃圾收集算法,以达到最好的收集性能

增理收集算法

  1. 需要程序将所拥有的内存空间分成若干分区。程序运行所需的存储对象会分布在这些分区中,每次只对其中一个分区进行回收操作,从而避免暂停所有正在运行的线程来进行回收,允许部分线程在不影响回收行为下保持运行,并且降低回收时间,增加程序响应速度。
  2. 总的来说,增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作。
  3. 因为线程切换和上下文转换的消耗,使得垃圾回收的成本上升,造成系统吞吐量的下降。

分区算法

  1. 为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次Gc所产生的停顿。
  2. 分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间。
  3. 每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。
posted @ 2021-12-09 21:33  翻蹄亮掌一皮鞋  阅读(54)  评论(0)    收藏  举报