参考书籍

1.垃圾回收算法手册  自动内存管理的艺术
2.垃圾回收的算法与实现

标记清扫回收

垃圾回收算法一般都是现在的自动内存管理系统中都会使用到的,例如python、go等语言都实现了自己的垃圾回收机制,从而解放了用户手动来管理内存的问题。一个自动内存管理系统一般都分为主要的三个操作:1.为新对象分配空间,2.确实已分配空间的对象是否存活,3.回收死亡对象占用的内存空间。通过这三种方式,来在程序运行的过程中,保持程序能够对资源有个比较良好的利用率,少占用不必要的内存,避免内存泄露等问题。标记清扫回收也只是其中的一种算法。

在实际的程序的运行过程中,如果在单线程的运行过程中,内存的申请过程和回收过程都在一个线程中完成不会有竞争的问题,但是如果在多线程的运行过程中,内存的申请过程与回收过程都可以在不同的线程中来执行,这样就会导致在回收的过程中有比较复杂的策略来保证内存申请过程、内存的回收过程的正确性。当前比较有名的STW(stop the world)的方式就是指,申请过程可以在多个不同的线程上运行,但只有一个回收过程线程,当进行回收过程的时候,申请过程所在的线程就停止运行,这样简化了整个内存管理的实现,比如早起版本的go语言就通过这个模型来实现的内存管理系统。

标记清扫回收的过程

标记清扫的过程主要分为三步,分别为分配、标记和清扫。

分配

如果在线程无法分配对象的时候,唤起回收器回收空余的内存,然后再次尝试申请内存分配对象,如果在回收完成之后仍然没有足够的内存来满足分配需求,则说明内存已经没有多余可用,内存爆满会引发一些异常的情况。相关的伪代码可参考如下(借鉴自书中伪代码):

New():ref <- allocate()if ref == null     // 如果申请的内存为空  则证明没有内存可使用需要回收一下collect()        // 收集回收ref <- allocate()  // 再次申请内存if ref == null     // 此时说明没有更多的内存可以申请,内存爆满error "Out of Memory"return refatomic collect():      // 回收过程标记在线程中是个原子操作markFromRoots()      // 从根部查找对分配的对象做标记sweep(HeapStart, HeapEnd)   // 回收释放的对象

#####标记

标记主要就是将当前仍在使用的对象标记为可用对象,搜索出那些没有进行标记的对象从而将该对象删除释放内存。

markFromRoots():initialise(worklist)                                 // 初始化一个空的列表保存标记后的对象for each fld in Roots                 // 通过根遍历所有的子节点ref <- *fld                                              // 获取引用的对象if ref != null && not isMarked(ref)   // 如果不为空并且没有被标记setMarked(ref)                                          // 设置标记add(worklist, ref)                                   // 添加到worklist列表中mark()                              // 循环遍历所有的worklistinitialise(worklist)worklist <- empty                         // 初始化一个空的列表mark():while not isEmpty(worklist)                      // 如果列表不空ref <- remove(worklist)               // 获取列表中的引用for each fld in Pointers(ref)          // 遍历该引用下所有的子引用child <- *fldif child != null && not isMarked(child)     // 如果没有标记则添加标记并添加到worklist中setMarked(child)add(worklist, child)

对于该单线程的回收器而言,主要是以深度优先遍历的方式,将标记这些能够遍历到的对象。标记过程相对比较直观,即先遍历整个roots,然后再工作列表中获取对应的子节点的对象的引用,然后对其所引用的其它对象进行标记,直到该工作列表为空位置,此时标记完成的结束条件就是工作列表为空,此时回收器就完成了将每个可达的对象的访问并标记,其余没有打上标记的对象就都是需要回收的对象。

清扫

在标记完成之后,在清扫阶段就主要是回收器将所有没有被标记的对象返还给分配器,在这一过程中,回收器会在进行一个线性扫描,即开始释放未标记的对象,同时清空存活对象的标记位以便下次回收过程复用。

sweep(start, end):                   // 开始清扫的起止位置scan <- start                        while scan < end                     // 如果还没有结束if isMarked(scan)             // 检查是否标记 如果标记unsetMarked(scan)     // 则设置为未标记else free(scan)         // 如果未标记则释放掉该对象scan <- nextObject(scan)  // 获取下一个对象

在这个实现的过程中,内存的布局过程需要满足一定的条件才能完成如上的执行流程,首先,回收不会移动的对象,内存管理必须能够控制堆的碎片的问题,否则会因为过多的内存碎片可能会导致分配器无法满足新分配请求从而增加垃圾回收的频率,再者,回收器需要能够遍历到每一个所有分配出去的对象,即对于给定的对象必须能够获取到下一个对象,因此需要获取较多的数据内存。

标记清除算法示例

package mainimport ("fmt""sync"
)type Obj struct {value interface{}size intprev *Objmark boolchildrens []*Obj
}type GC struct {mutx sync.Mutexroot *Obj
}func(gc *GC) register_pool(obj *Obj){gc.mutx.Lock()gc.root.childrens = append(gc.root.childrens, obj)gc.mutx.Unlock()
}func(gc *GC) search_node_mark(node *Obj){if node == nil{return}for _, n := range node.childrens {if n.value == nil {n.mark = true}gc.search_node_mark(n)}
}func(gc *GC) search_available_obj(node *Obj)*Obj{if node == nil {return nil}for _, n := range node.childrens {if n.value == nil {n.size = 0return n}r := gc.search_available_obj(n)if r != nil {r.size = 0return r}}return nil
}func(gc *GC) mark(){gc.search_node_mark(gc.root)
}type Worker struct {node *Objgc *GC
}func(w *Worker) alloc_mem(size int, val interface{}) *Obj{var obj *Objobj = w.gc.search_available_obj(w.node)if obj != nil {obj.size = sizeobj.value = valreturn obj}obj = &Obj{size: size,value: val,mark: false,childrens: []*Obj{},}w.node.childrens = append(w.node.childrens, obj)return obj
}func(w *Worker) free_mem(obj *Obj){obj.value = nil
}func NewGC()* GC{return &GC{root:&Obj{size: 0,value: "root",mark: false,childrens: []*Obj{},},}
}func NewWorker(gc *GC)* Worker{return &Worker{&Obj{size: 0,value: "",mark: false,childrens: []*Obj{},},gc,}
}func main() {gc := NewGC()w1 := NewWorker(gc)gc.register_pool(w1.node)obj1 := w1.alloc_mem(10, "obj1")obj2 := w1.alloc_mem(20, "obj2")fmt.Println(obj1, obj2)w1.free_mem(obj1)gc.mark()for _, n := range w1.node.childrens {fmt.Println("obj ", n)}obj3 := w1.alloc_mem(30, "new val OBj3")fmt.Println("obj3 ", obj3)gc.mark()for _, n := range w1.node.childrens {fmt.Println(n)}
}

该段代码,只是在简单的模拟了一下,gc执行过程中的申请内存,释放内存,标记的一个简单思路,即通过一个列表加数组的结构把所有的使用的对象,通过一个层级结构来进行连接,但是该结构中需要自己保证在申请内存的时候的对象不能形成类似与联通图的情况,否则扫描标记的情况就会一直循环下去,在查找的过程中使用深度优先的策略,去标记未使用的obj和查找当前可用的obj。如果该内容由多个协程来同时进行申请或者释放obj,最后垃圾回收的代码就需要进行STW来进行操作,即所有mark的块的清理的工作都必须在其他协程不工作的前提下来完成,这个示例代码看后续能否再改进一下,因为示例代码压根就没有collect的过程。

总结

本文主要是翻阅内存管理相关的书籍,记下相关的内容来加深印象,标记清扫回收的最基本的思路,就是每次申请内存的时候,检查一下当前是否还有可以使用的内存(前提是提前申请一大块内存)如果没有则进行垃圾回收,此时的垃圾回收会依次遍历已经分配的内存块是否存活,如果不存活则标记等待下一步的回收操作,最终来获取一块可用的内存返回,这里面还有还多如申请内存的内存对齐,内存碎片的管理等等工作都是比较深入的技术细节点,大家可以自行查阅相关知识。由于本人才疏学浅,如有错误请批评指正。

垃圾回收算法-标记清扫回收相关推荐

  1. 垃圾回收算法——标记—清扫回收算法

    理想的垃圾回收的目的是回收程序不再使用的对象所占用的空间,任何自动内存管理系统都面临三个任务: 为对象分配空间: 确定存活对象: 回收死亡对象所占用的空间: 这些任务并非相互独立,特别是回收空间的方法 ...

  2. 【Java 虚拟机原理】垃圾回收算法 ( 标记-清除算法 | 复制算法 | 标记-整理算法 )

    文章目录 总结 一.标记-清除算法 二.复制算法 三.标记-整理算法 总结 常用的垃圾回收算法 : 标记-清除算法 ; 复制算法 ; 标记-整理算法 ; 这些算法没有好坏优劣之分 , 都有各自的 优势 ...

  3. 2、垃圾回收算法(标记清除算法、复制算法、标记整理算法和分代收集算法),各种垃圾收集器讲解(学习笔记)

    2.垃圾回收概述 2.1.垃圾回收算法 2.1.1.垃圾回收算法-标记清除算法 2.1.2.垃圾回收算法–复制算法 2.1.3.垃圾回收算法–标记整理算法和分代收集算法 2.1.4.垃圾回收算法–Se ...

  4. java gc回收算法_Java GC回收算法-判定一个对象是否可以回收

    开源推荐 推荐一款一站式性能监控工具(开源项目) Pepper-Metrics是跟一位同事一起开发的开源组件,主要功能是通过比较轻量的方式与常用开源组件(jedis/mybatis/motan/dub ...

  5. JVM垃圾回收算法标记清除和复制算法

    标记清除算法 当堆中的有效空间被耗尽时,JVM就会停止整个程序(也被称为stop the world),然后开始两项工作.一是:标记, 而是:清除 标记 遍历所有GC Roots,将所有GC Root ...

  6. 《垃圾回收算法手册 自动内存管理的艺术》——标记整理与复制式回收(笔记)

    文章目录 三.标记-整理回收 3.1 双指针整理算法(任意顺序) 原理 步骤 3.2 Lisp 2算法(滑动顺序) 原理 步骤 3.3 引线整理算法(滑动顺序) 原理 步骤 3.4 单次遍历算法(滑动 ...

  7. Java-JVM虚拟机内存垃圾回收机制gc入门:引用类型,对象标记算法,回收算法,常见的 garbage collector

    文章目录 GC的优缺点 引用的四种类型 对象标记算法 引用计数法 可达性分析法 回收算法 标记-清除算法(Mark-Sweep) 复制算法 标记-整理算法(Mark-Compact) 分代收集算法 常 ...

  8. 【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )

    文章目录 一. 分代收集算法 二. 垃圾回收器 / 收集器 ( GC ) 三. 串行收集器 ( Serial ) 四. ParNew 收集器 五. Parallel Scavenge 收集器 六. C ...

  9. 【Android 内存优化】垃圾回收算法 ( 内存优化总结 | 常见的内存泄漏场景 | GC 算法 | 标记清除算法 | 复制算法 | 标记压缩算法 )

    文章目录 一. 内存优化总结 二. 常见的内存泄漏场景 三. 内存回收算法 四. 标记-清除算法 ( mark-sweep ) 五. 复制算法 六. 标记-压缩算法 一. 内存优化总结 内存泄漏原理 ...

  10. JVM垃圾回收算法及垃圾回收器

    目录 一.垃圾回收GC (Garbage Collection) 1.1 垃圾回收介绍: 1.2  如何判断对象是否存活(被使用) 1.2.1 引用计数算法(已经废弃) 1.2.2 可达性分析 二.垃 ...

最新文章

  1. python round函数_Python round() 函数
  2. 让 Git 全局性的忽略 .DS_Store
  3. layui table 单元格适应宽高
  4. 牛客网暑期ACM多校训练营(第九场)
  5. 树莓派做一个dns缓存
  6. S2-MLPV2:目前最强的视觉MLP架构,空降榜一,达到83.6% Top-1准确率
  7. Intellij idea 插件 | 超越鼓励师 吐槽
  8. 多种方法破解Windows 系统密码
  9. EXCHANGE 2016证书续期
  10. 根据ISBN查询图书信息
  11. 阿里云服务器搭建及域名申请攻略
  12. 动态切换 web 报表中的统计图类型
  13. 360 安全入门 · 课程测试
  14. font face=微软雅黑 color=DodgerBlue*IncomesESL Analy*/font
  15. 视觉SLAM十四讲-第九讲例程运行出错
  16. 《Microduino实战》——1.1 什么是开源
  17. 阿里云服务器入门教程 (图文教程)
  18. client_loop: send disconnect: Broken pipe
  19. 《Python语言程序设计》王恺 王志 机械工业出版社 第八章 多线程与多进程 课后习题答案
  20. 封闭式基金高折价蕴涵巨大投资机会

热门文章

  1. Java中区分中英文
  2. 漏洞利用六:Linux系统漏洞利用
  3. Codecademy.com学习Python
  4. 基于时空大数据的GIS技术,推动网格化管理创新发展
  5. tensorflow笔记之二十八——带掩码的损失函数
  6. 圣诞帽php,微信小程序“圣诞帽”的实现思路详解
  7. c语言程序设置存根,gmock可以用于存根C函数吗?
  8. STM32掌机教程8,背景音乐
  9. 从Spring为什么要用IoC的支点,我撬动了整个Spring的源码脉络
  10. essay--网络常用省略语大全(ZT)