• 世界上第一个GC算法,由 JohnMcCarthy 在1960年发布。

标记-清除算法由标记阶段和清除阶段构成。

  1. 标记阶段就是把所有的活动对象都做上标记的阶段。

    • 标记阶段就是“遍历对象并标记”的处理过程。
    • 标记阶段经常用到深度优先搜索。
    1. mark_pahase(){
    2. for(r : $roots)
    3. mark(*r)
    4. }
    5. mark(obj){
    6. if(obj.mark == FALSE)
    7. obj.mark = TRUE
    8. for(child : children(obj))
    9. mark(*child)
    10. }
  2. 清除阶段就是把那些没有标记的对象,也就是非活动对象回收的阶段。

    • 清除阶段collector会遍历整个堆,回收没有打上标记的对象(即垃圾)。
    • 内存的合并操作也是在清除阶段进行的。
    1. sweep_phase(){
    2. sweeping = $heap_start
    3. while(sweeping < $heap_end)
    4. if(sweeping.mark == TRUE)
    5. sweeping.mark = FALSE
    6. else
    7. sweeping.next = $free_list
    8. $free_list = sweeping
    9. sweeping += sweeping.size
    10. }

1. 分配

  • 分配指将回收的内存空间进行再利用。

    1. -> 伪代码描述内存分配
    2. new_obj(size){
    3. chunk = pickup_chunk(size, $free_list)
    4. if(chunk != NULL)
    5. return chunk
    6. else
    7. allocation_fail() # 大招,销毁并释放全部空间
    8. }

2. 合并

  • 合并指将连续的小分块连在一起形成一个大分块

    1. -> 伪代码描述合并操作
    2. sweep_phase() {
    3. sweeping = $heap_start
    4. while(sweeping < $heap_end)
    5. if(sweeping.mark == TRUE)
    6. sweeping.mark = FALSE
    7. else
    8. if(sweeping == $free_list + $free_list.size)
    9. $free_list.size += sweeping.size
    10. else
    11. sweeping.next = $free_list
    12. $free_list = sweeping
    13. sweeping += sweeping.size
    14. }


优点

  1. 实现简单
  2. 与保守式GC算法兼容

缺点

  1. 碎片化(fragmentation)

    • 使用过程中会逐渐产生被细化的分块
  2. 分配速度

    • 分块不连续,每次分配都必须遍历空闲链表,以便找到足够大的分块。
    • 最糟的情况就是每次分配都要遍历全部空闲链表
  3. 与写时复制技术(copy-on-write)不兼容

    • 写时复制技术在重写时要将共享空间数据复制为自己的私有空间数据后,再对私有空间数据进行重写。
    • 而标记-清除算法需要频繁的设置所有活动对象的头的标志位,这样就会频繁发生本不应该发生的复制,压迫到内存空间。

为了解决分配速度的问题, 人们提出了两种方法

  1. 使用多个空闲链表(multiple free-list)

    • 类似于建立索引的方法。

    • 为了防止空闲链表(也就是索引)的数组过大的问题,通常会给分块大小设定一个上限。

    • 大于这个上限的按照一个空闲链表处理。

      • ex.
      • 设置上限为100
      • 那么准备1~100及大于等于101个字的100个空闲链表就可以了。
    1. -> 伪代码描述使用多个空闲链表的内存分配
    2. new_obj(size){
    3. index = size / (WORD_LENGTH / BYTE_LENGTH)
    4. if(index <= 100)
    5. if($free_list[index] != NULL)
    6. chunk = $free_list[index]
    7. return chunk
    8. else
    9. chunk = pickup_chunk(size, $free_list[101])
    10. if(chunk != NULL)
    11. return chunk
    12. allocation_fail() # 大招,销毁并释放全部空间
    13. }
    1. -> 伪代码描述使用多个空闲链表的内存合并
    2. sweep_phase() {
    3. for(i : 2...101)
    4. $free_list[i] = NULL
    5. sweeping = $heap_start
    6. while(sweeping < $heap_end)
    7. if(sweeping.mark == TRUE)
    8. sweeping.mark = FALSE
    9. else:
    10. index = size / (WROD_LENGTH / BYTE_LENGTH)
    11. if(index<=100)
    12. sweeping.next = $free_list[index]
    13. $free_list[index] = sweeping
    14. else
    15. sweeping.next = $free_list[101]
    16. $free_list[101] = sweeping
    17. sweeping += sweeping.size
    18. }
  2. BiBOP(Big Bag Of Pages)法

    • BiBOP法是指将大小相近的对象整理成固定大小的块进行管理的做法
    • 对此我们可以把堆分割成固定大小的块,让每个块只能配置同样大小的对象。

  • 缺点

    BiBOP并不能完全消除碎片化。可能出现某一个块中活动对象过少的问题。

    1. 比如在全部用于2个字的块中,只有一个活动对象
    2. BiBOP法原本是为了消除碎片化,提高堆的使用效率而采用的方法。
    3. 但像上面这样,在多个块中分散残留着同样大小的对象,反而会降低堆的使用效率。

为了解决与写时复制不兼容的问题,则采取位图标记的方法

  • 位图标记

    位图标记的方法就是只收集各个对象的标志位并表格化,不跟对象一起管理。在标记的时候,不在对象的头里标记,而是在这个表格中的标记。

    像这样集合了用于标记的位的表格称为“位图表格”。

    • 优点

      1. 与写时复制技术兼容
      2. 清除操作更高效
    • 注意

      • 有多个堆的话,一班会为每个堆都准备一个位图表格

延迟清除法(Lazy Sweep)

延时清除法是缩减因清除操作而导致的mutator最大暂停时间的方法。在标记操作结束后,不立即进行清除操作。

  1. -> 伪代码描述延时清除法中的分配操作
  2. new_objsize){
  3. chunk = lazy_sweep(size)
  4. if(chunk != NULL)
  5. reutrn chunk
  6. make_phase()
  7. chunk = lazy_sweep(size)
  8. if(chunk != NULL)
  9. return chunk
  10. allocation_fail()
  11. }
  12. lazy_sweep(size){
  13. while($sweeping < $heap_end)
  14. if($sweeping.mark == TRUE)
  15. $sweeping.mark == FALSE
  16. else if($sweeping.size >= size)
  17. chunk = $sweeping
  18. $sweeping += $sweeping.size
  19. return chunk
  20. $sweeping += $sweeping.size
  21. $sweeping = $heap_start
  22. return NULL
  23. }
  • lazy_sweep() 函数会一直遍历堆,知道找到大于等于所申请大小的分块为止。
  • 在找到合适的分块时会将其返回。
  • 但是在这里$sweeping 变量时全局变量。也就是说遍历的开始为止位于上一次清除操作中发现的分块的右边。
  • 当lazy_sweep()函数遍历到堆的最后都没有找到分块时,会返回NULL。
  • 因为延时清除法不是一下遍历整个堆,它只在分配时执行必要的遍历,所以可以压缩因清除操作而导致的mutator的暂停是时间。这就是“延时”清除操作的意思。
缺点

延时清除的效果是不均衡的

如图:

  • 如果垃圾和活动对象在堆中的分布都变成连续的,那么程序在垃圾部分(1标示的位置)能马上获得分块。而一旦程序进入活动对象周围(2标示的位置),就怎么都无法获得分块了。这样就增加了mutator的暂停时间。

1. GC标记-清除算法(Mark Sweep GC)的更多相关文章

  1. 《垃圾回收的算法与实现》——GC标记-清除算法

    基本算法 标记-清除算法由 ==标记阶段== 和 ==清除阶段== 构成. 标记即将所有活动的对象打上标记. 清除即将那些没有标记的对象进行回收. 标记与清除 遍历GC root引用,递归标记(设置对 ...

  2. Java GC 标记/清除算法

    1) 标记/清除算法是怎么来的? 我们在程序运行期间如果想进行垃圾回收,就必须让GC线程与程序当中的线程互相配合,才能在不影响程序运行的前提下,顺利的将垃圾进行回收. 为了达到这个目的,标记/清除算法 ...

  3. Mark Sweep GC

    目录 标记清除算法 标记阶段 深度优先于广度优先 清除阶段 分配 First-fit.Best-fit.Worst-fit三种分配策略 合并 优点 实现简单 与保守式GC算法兼容 缺点 碎片化 分配速 ...

  4. Reference Counting GC (Part two :Partial Mark & Sweep)

    目录 部分标记清除算法 前提 dec_ref_cnt()函数 new_obj()函数 scan_hatch_queue()函数 paint_gray()函数 scan_gray()函数 collect ...

  5. (转)jvm具体gc算法介绍标记整理--标记清除算法

    转自:https://www.cnblogs.com/ityouknow/p/5614961.html GC算法 垃圾收集器 概述 垃圾收集 Garbage Collection 通常被称为“GC”, ...

  6. JVM内存管理------GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  7. GC算法精解(五分钟让你彻底明白标记/清除算法)

    GC算法精解(五分钟让你彻底明白标记/清除算法) 相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底 ...

  8. JVM内存管理之GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  9. JVM之GC算法、垃圾收集算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

    标记-清除算法 此垃圾收集算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象,它的标记过程前面已经说过——如何判断对象是否存活/死去 死去的对象就会 ...

随机推荐

  1. USER 指定当前用户,希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu

    USER 指定当前用户 格式:USER <用户名>[:<用户组>] USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层.WORKDIR 是改变工作目录,US ...

  2. hash路由

    class HashRouter{ constructor(){ //用于存储不同hash值对应的回调函数 this.routers = {}; window.addEventListener('ha ...

  3. event.clientX和event.clientY

    event.clientX.event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条.IE事件和标准事件都定义了这2个属性 event.pageX ...

  4. 【PAT甲级】1001 A+B Format (20 分)

    题意:给两个整数a,b,计算a+b的值并每三位用逗号隔开输出(−1e6​​≤a,b≤1e6​​) AAAAAccepted code: #include<bits/stdc++.h> us ...

  5. I/O-<文件读写、输出>

    读写 FileInputStream fis=null; fis=new FileInputStream("D://2016.txt");//初始文件位置 int i=0; byt ...

  6. 爬虫(十四):Scrapy框架(一) 初识Scrapy、第一个案例

    1. Scrapy框架 Scrapy功能非常强大,爬取效率高,相关扩展组件多,可配置和可扩展程度非常高,它几乎可以应对所有反爬网站,是目前Python中使用最广泛的爬虫框架. 1.1 Scrapy介绍 ...

  7. hutoolJava工具类的使用

    前言 安装 友情开源项目 Hutool相关博客(软文) 捐赠使用公开 核心(Hutool-core) 克隆 支持泛型的克隆接口和克隆类 类型转换 类型转换工具类-Convert 自定义类型转换-Con ...

  8. Java通过反射实现实例化

    public static void main(String[] args) throws Exception { User user= (User) test(User.class); System ...

  9. 利用DOCKER实现云桌面的开发环境初步设想

    想法阶段,持续更新中 一.准备一台开发专用服务器 二.建立企业私有镜像仓库 三.建立开发环境镜像并提交到私有镜像仓库 开发镜像的要求: 1.安装vnc服务,ssh服务 vnc密码的设定 2.安装开发环 ...

  10. js实现超范围的数相加

    在js中能表示的最大安全整数是 9007199254740991,可以用API Number.MAX_SAFE_INTEGER 看一下  超出范围就会发生精度丢失,像这样 解决方法: 相当于一个字符串 ...