GC算法-标记清除算法
概述
标记清除算法, 描述起来很简单, 从名字上就能看出, 分为两个阶段:
- 标记阶段: 遍历所有对象, 将活动对象都打上标记
- 清除阶段: 遍历堆, 将没有标记的对象释放掉.
介绍完毕, 本文结束. 开玩笑, 确实看上去很简单啦. 那就具体思考一下实现吧.
实现
介绍写的很清楚了, 实现也是两个阶段呗, 先打tag, 后清除.
标记
寻找所有的活动对象, 要从一个起点开始, 根集合(包括栈、常量池等等), 然后一层一层找下去. 简单来说就像这样:
add_mark(obj){
// 防止重复标记
if(obj.mark == true) return;
obj.mark = true
// 遍历所有子对象
for(o in objs){
add_mark(o)
}
}
将根基合的所有对象都调用一遍, 标记完成.
清除
标记时遍历的是活动对象, 清除阶段呢? 遍历堆. 将堆上所有非活动对象清除. 就比如:
当然, 其中的free
函数也不是简单的将地址回收, 而是将其记录到一个链表中, 以方便下次申请内存. 实现大概如下:
free(p){
// 这里有一个全局变量, 保存链表头: FREE_HEAD
// 若当前对象和上一次回收的对象是连续内存, 直接合并
if(FREE_HEAD + FREE_HEAD.size == p){
FREE_HEAD.size += p.size
}else{
p.next = FREE_HEAD
FREE_HEAD = p
}
}
这样, 申请内存时遍历FREE_HEAD, 找到大小合适的分块. 若没有找到, 就动态扩容咯. 这里其实还有一个优化的小方向, 开始的时候忘记了. 为了提高查找内存时速度, 可以将空闲链表按照大小进行区分, 这样, 需要多大的内存, 直接到对应的链表中找就行了.
虽然实现写的很粗糙, 但大致意思有了.
问题
1.内存的碎片化
从上面可以看到, 只对内存进行了清除, 但是没有整理. 而内存的申请有事动态的, 就会导致出现很多离散的小片空闲内存. 极端情况甚至可能内存中还有200mb的空闲内存, 但是申请个10kb的空间却找不到.
2.暂停时间长
其暂停时间与堆的大小成正比, 堆越大, 遍历清除耗费的时间就越久.
为了解决标记清除算法的问题, 衍生出了位图标记法
, BiBOP法
,延迟清除算法
等等个人感觉很鸡肋(好吧, 或许是我未得其精髓).
为了解决标记清除的问题, 有衍生出了 标记复制
, 标记整理
算法, 之后再议.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY