当前位置: 首页 > news >正文

不考虑分配与合并情况下,GO实现GCMarkSweep(标记清楚算法)

观前提醒

  • 熟悉涉及到GC的最基本概念到底什么意思(《垃圾回收的算法与实现》
  • 我用go实现(因为其他的都忘了,(╬◣д◢)ムキー!!

源码地址(你的点赞,是我开源的动力)kokool/GCByGo: 《垃圾回收的算法与实现》有感而发 (github.com)

流程

轮到具体实现了,最基本的就是总体划分成两个阶段。

func main(){mark_phase()sweep_phase()
}

肯定会得到疑问,我们该具体选择哪种数据结构实现如下的选项?它们需不需要面向对象?

  • 根(roots)?
  • 堆(Heap)?
  • 空闲链表(free-list)?
  • 对象/分块(Object/Chunked)?

到底是理解流程?

其实就是把从roots从上往下指向的object进行mark,这些被markobject就成为了active object

而不满足条件的就变成了non-active object,接着把所有mark清掉。

伪代码

标记阶段

mark_phase()的伪代码。

mark_phase(){for(r : $roots)//mark(obj)的形参是对象mark(*r)
}

用go实现的初步代码

func mark_phase() {for r := range roots {//获得的r是index,*r则要变成对象,而堆放的是活动对象/非活动对象mark(*r)}
}

mark()的伪代码:就是简单的递归,另外把标记mark属性改名成marked以防混淆

mark(obj){//检查对象没被标记if(obj.mark == FALSE)//那么就标记它obj.mark = TRUE//标记后对该对象的指针数组继续访问下一个obj(想象成树结构),继续标记未标记完成的objfor(child : children(obj))mark(*child)
}

用go实现的初步代码

func mark(obj *Object){if obj.marked==false {obj.marked=true//注意是一定要想象成树结构,因为你指向的不是只有一个对象for child:=range obj.children {//注意这个*child是对象mark(*child)}}
}

清除阶段

collector 会遍历整个堆,回收没有打上标记的对象

sweep_phase()

sweep_phase(){//$heap_start为index,是堆首地址sweeping = $heap_start//在没遍历到底时while(sweeping < $heap_end)//如果被标记了if(sweeping.mark == TRUE)//你都回收了,那么肯定去除标记sweeping.mark = FALSE//如果找到非活动对象else//把没标记的对象拉到空闲链表sweeping.next = $free_list$free_list = sweeping//空闲链表的长度改变sweeping += sweeping.size
}

分析上面的伪代码,这里的sweeping含有两种属性

  • index
  • object

$free_list的属性有

  • length->index
  • object.next

总之sweeping变得多余了,$free_list既带有空闲链表的长度和下标的属性,更是可以抽象成一个表结构。

那么说这么多废话,写的go的初步代码为

func sweep_phase() {free_list = head_startheap=[]obj//书本1.4for i := range heap {if heap[i].marked == true {heap[i].marked = false} else {//move_pointer(*heap[i])//去除指针指向,伪代码隐含了这层意思,无用的指针是要清掉的heap[i].next_freeIndex = free_list//指向空闲链表对应的位置free_list = i//长度改变}}
}

看懂上面的废话就觉得可以了?实际上计算机可是比人严谨多了,99%的错误都是由人造成的

尽管不想承认,最终还是感谢参考的内容,可恶的霓虹人,希望各位给他的github.com点赞。

在上图中并不清楚roots指向的各个object中的域到底存放什么信息?因为书本只表示了指针pointer

经过我本人的两天的debug,发现如果使用B树的结构,让每个object既存放data又存放指向其他objectpointer,造成的结果就是:

  • 查找效率低
  • data被计算机理解成pointer,之后递归就面临out of index或者栈溢出->判断条件if marked==false会规避掉这风险->找不到,Go语言贴心地帮你point成默认的0值(真的,它太,我哭死!

总而言之,因为没有正确解决掉识别data还是pointer的问题,就造成这样的结果。

既然如此,那么干脆就不识别了,直接分开就对了,然后使用如下的结构。

B+树

性质

  • 叶子节点存放data,非叶子节点存储key关键字,而我们的key就是要指向active object用的pointer
  • 所有叶子节点增加一个链表指针,正好表示free_list

代码

目录结构

在这里插入图片描述

具体实现

先让我们有个共识

  • -1数值表示指向null
  • -100数值表示不指向任何对象,即叶子节点
  • 域本身要通过objectflag判断是表示成pointer还是data

根据算法篇 第1章的知识与上面的伪代码内容,就应该清楚我们应该如何实现Object,Heap,roots的结构体了

GCByGO\GCMakSweep\notConsidered\PKG\object.go

package PKGtype Object struct {//利用go的空接口类型表示指针数组,当然你也可以用[]string,用map[]其实更合适//总之我们要实现的要求如下://key为本对象的indexchildren       []int//[]stringnode_type      NodeType // flagmarked         bool     //是否被标记size           int      //域的大小,假设一个指针占1字节的域next_freeIndex int      //free_list的指向
}var roots []int//既然堆选择的是存放对象,那么让roots代表指向堆中对象的下标var free_list int//设个常量,自己看着办
const (HEAP_SIZE = 7 //就拿书本的例子
)var heap [HEAP_SIZE]Object //书本1.4,堆存放的就是对象type NodeType string//专门区分到底是放pointer还是data
func newNodeType(node_type string) NodeType {if node_type == "Key" || node_type == "Data" {return NodeType(node_type)} else {panic("error type")}
}

那么roots是什么?在书本1.8作者就明示了

在这里插入图片描述

GCByGO\GCMakSweep\notConsidered\PKG\mark_sweep.go

  • Mark_phase()函数
func Mark_phase() {for r := range roots {var heap_index = roots[r]mark(&heap[heap_index])}
}
  • mark()函数
func mark(obj *Object) {if obj.marked == false {obj.marked = trueif obj.node_type == "Key" {for i := range obj.children {//共有三种写法实现字符串转整数//strconv.Atoi(obj.children[i])//fmt.Sprintf("%d",obj.children[i])// index,_:=strconv.ParseInt(obj.children[i],10,64)index := obj.children[i]fmt.Println(index)mark(&heap[index])}}}
}
  • Sweep_phase()函数
func Sweep_phase() {//默认-1表示指向nullfree_list = -1for id := range heap {if heap[id].marked == true {heap[id].marked = false} else {move_pointer(&heap[id])heap[id].next_freeIndex = free_listfree_list = id}}
}//当这是我们要清除标记的情况:
//	字符串就要设为空字符串切片
//	整数数组则写充满-1的整数切片,因为我们默认-1
//	当然我们其实还有很多表示方法,看自己喜欢,
func move_pointer(obj *Object) {// obj.children = []string{""}obj.children = []int{-1}
}

数据集测试

GCByGO\GCMakSweep\notConsidered\PKG\create_data.go

  • Init_data()函数:创建数据集
func Init_data() {//初始化堆中的所有对象,对于多出来的对象则进行默认操作表示成非活动对象h := Object{marked: false, node_type: "Null", children: []int{-1}, size: 0, next_freeIndex: -100}for i := range heap {heap[i] = h}var key_type = newNodeType("Key")var data_type = newNodeType("Data")//对象指向的对象(活动对象)heap[1] = Object{children: []int{11}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}heap[3] = Object{children: []int{5, 4}, node_type: key_type, marked: false, size: 2, next_freeIndex: -100}heap[4] = Object{children: []int{44}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}heap[5] = Object{children: []int{55}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}//对象指向的对象(非活动对象)heap[0] = Object{children: []int{20}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}heap[2] = Object{children: []int{22}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}heap[6] = Object{children: []int{66}, node_type: data_type, marked: false, size: 2, next_freeIndex: -100}//roots指向的对象roots = []int{1, 3}
}
  • Print_data()函数:输出标记阶段结束后的堆状态
func Print_data() {for i := range heap {fmt.Printf("--- object %d ---\n", i)fmt.Println(heap[i])}
}

GCByGo\GCMakSweep\main.go

main()修改

package mainimport (MS "GCByGo/GCMarkSweep/notConsidered/PKG""fmt"
)func main() {MS.Init_data()fmt.Println("### init ###")MS.Print_data()MS.Mark_phase()fmt.Println("### mark phase ###")MS.Print_data()MS.Sweep_phase()fmt.Println("### sweep phase ###")MS.Print_data()
}

结果

执行GC前堆的状态,init阶段

在这里插入图片描述

请添加图片描述

标记阶段结束后的堆状态,mark阶段

在这里插入图片描述

请添加图片描述

清除阶段结束后的堆状态,sweep阶段

在这里插入图片描述

请添加图片描述

满足书本的最终结果

请添加图片描述

参考

[1]中村成洋《垃圾回收的算法与实现》

[2] github.com垃圾回收

[3] https://zhuanlan.zhihu.com/p/361287050

相关文章:

不考虑分配与合并情况下,GO实现GCMarkSweep(标记清楚算法)

观前提醒 熟悉涉及到GC的最基本概念到底什么意思&#xff08;《垃圾回收的算法与实现》&#xff09;我用go实现&#xff08;因为其他的都忘了&#xff0c;(╬◣д◢)&#xff91;&#xff77;&#xff70;!!&#xff09; 源码地址&#xff08;你的点赞&#xff0c;是我开源的…...

利用HGT聚类单细胞多组学数据并推理生物网络

单细胞多组学数据允许同时对多种组学数据进行定量分析&#xff0c;以捕捉复杂的分子机制和细胞异质性。然而现有的工具不能有效地推断不同细胞类型的活性生物网络以及这些网络对外部刺激的反应。 来自&#xff1a;Single-cell biological network inference using a heterogen…...

杂记——18.VSCode的下载及使用

这篇文章&#xff0c;我们来讲一下VSCode&#xff0c;讲一下如何下载及使用VSCode 目录 1.VSCode的下载 1.1VSCode的简介 1.2VSCode的下载与安装 1.2.1下载 1.2.2安装 2.VSCode的使用 2.1界面 2.2基础设置 2.3禁用自动更新 2.3自动保存设置 2.4Vscode更换主题 2.5…...

【独家】华为OD机试 - 最少停车数(C 语言解题)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

顶级动漫IP加持之下,3A策略游戏Mechaverse如何改变GameFi

2021年是元宇宙发展的元年&#xff0c;元宇宙与GameFi创造了一波又一波市场热点。在经历第一波热潮之后&#xff0c;元宇宙的到来让不少人看到了加密市场的潜力&#xff0c;同时大家也意识到这将是未来的重要方向。如何将元宇宙推向更广阔的市场&#xff0c;让更多人能够轻松进…...

一款丧心病狂的API测试工具:Apifox!

你好&#xff0c;我是测试开发工程师——凡哥。欢迎和我交流测试领域相关问题&#xff08;测试入门、技术、python交流都可以&#xff09; 我们平时在做接口测试的时候&#xff0c;对于一些常用的接口测试工具的使用应该都非常熟悉了&#xff1a; 接口文档&#xff1a;Swagge…...

【前端学习】D2-2:CSS基础

文章目录前言系列文章目录1 Emmet语法1.1 快速生成HTML语法结构1.2 快速生成CSS样式语法1.3 快速格式化代码2 CSS复合选择器2.1 什么是复合选择器2.2 后代选择器&#xff08;*&#xff09;2.3 子选择器2.4 并集选择器&#xff08;*&#xff09;2.5 伪类选择器2.6 链接伪类选择器…...

Flink / Scala 实战 - 19.ProcessFunction 删除 key 的上一个定时器 TimeTimer

一.引言 ProcessFunction 原始执行状态为每个 key 注册一个较长时间 TimeTimer 并在这期间将所有对应 key 的数据都收集起来,到期完成触发。现在接到新的需求,要求判断数据类型,当特殊标识的数据到达后,需要将 TimeTimer 到期的时间提前。因此需要删掉当前 key 之前注册的老…...

MSTP基础

MSTP基础引入背景技术概览PVSTP&#xff08;过渡&#xff09;MSTP单生成树的缺陷1&#xff1a;部分VLAN不通单生成树的缺陷2&#xff1a;无法实现流量的负载分担多生成树解决单生成树实例引入背景 RSTP在STP基础上进行了改进&#xff0c;实现了网络拓扑快速收敛。但由于局域网…...

当ChatGPT遇见stable-diffusion,你不敢相信的创意艺术之旅!

前言 欢迎来到一场创意的旅程&#xff0c;这里将聚焦于 ChatGPT 和 stable-diffusion 这两个令人激动的技术。在这篇文章中&#xff0c;我们将会探索这两种技术如何结合使用&#xff0c;为艺术创作带来全新的可能性。我们将探讨如何利用 ChatGPT 生成富有想象力的创意&#xf…...

一文搞定!postman接口自动化测试【附项目实战详解】

目录&#xff1a;导读 | 接口结果判断 功能区 脚本相关 代码模板 | 集合(批量)测试 变化的参数数据 定期任务 接口执行顺序 数据传递 | 解决依赖问题 假设场景 Postman 中的操作 运行 写在最后 附带项目实战教程地址&#xff1a;postman接口自动化测试使用教程项…...

ctfshow【菜狗杯】wp

文章目录webweb签到web2 c0me_t0_s1gn我的眼里只有$抽老婆一言既出驷马难追TapTapTapWebshell化零为整无一幸免无一幸免_FIXED传说之下&#xff08;雾&#xff09;算力超群算力升级easyPytHon_P遍地飘零茶歇区小舔田&#xff1f;LSB探姬Is_Not_Obfuscateweb web签到 <?ph…...

旋转数组的几种做法

千淘万浪虽辛苦&#xff0c;吹尽黄沙始到金。 ——刘禹锡 第一种方法&#xff1a;遍历整个数组 题目描述&#xff1a; 一个数组A中存有N (N>0) 个整数&#xff0c;允许使用另外数组&#xff0c;将每个整数循环向右移动M(M>0)个位置。如果需要…...

创建虚拟机、添加镜像以及配置虚拟机

一、创建虚拟机 1、点击 “创建新的虚拟机” 2.选择“自定义配置” 到后面可以选择硬件的类型 3.默认值就行 4.选择 “稍后安装操作系统” 5.操作系统选择 “Linux”&#xff0c;版本结合镜像自行选择 6. 虚拟机的名称自行定义&#xff0c; 就是上述显示出来的名称。 虚拟机…...

Godot Engine 4.0横空出世,Vulkan大怪兽加持,画质提升简直亮瞎眼

【CSDN 编者按】经历了漫长的等待&#xff0c;万众瞩目的 Godot Engine 4.0 正式版在其 3.0 版本发布 5 年以后&#xff0c;终于带着海量令人兴奋的新功能横空出世&#xff01; 整理 | 开发游戏的老王 责编 | 王子彧 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09…...

CorelDRAWX4的VBA插件开发(四十五)建立类(2)汇总相似功能简化重复代码:一键建立设计外框加出血线和等分折页线

在上一节中已经建立好了类,那么这一节我们来调用它,先建立一个面板 然后修改框体名称 然后从左侧新建一些按钮并且以拼音为结尾进行命名 Private Sub CheckBox2_zheYe_Click() 鼠标按下几折页单选时触发If Me.CheckBox2_zheYe ThenMe.TextBox3_zheYeShu.Enabled True 让右…...

我的十年编程路 2017年篇

2016和2017&#xff0c;这两年是我飞速发展的两年。一方面是技术、工作能力&#xff0c;另一方面是对人生的思考。 随着技术能力的不断提升&#xff0c;博客也随之更新。在2017年伊始&#xff0c;我收到了CSDN学院的讲师邀请函。没错&#xff0c;那个时候我就有机会做视频课了…...

hadoop有多个输入路径怎么处理

在Hadoop中&#xff0c;可以使用FileInputFormat的addInputPath方法来添加多个输入路径。以下是实现步骤&#xff1a;创建一个Job对象&#xff0c;并设置相关的参数和配置信息。调用FileInputFormat的addInputPath方法添加输入路径。例如&#xff1a;FileInputFormat.addInputP…...

day6 ServletContext

ServletContext 一个Servlet对象对应一个ServletConfig。100个Servlet对象则对应100个ServletConfig对象。 只要在同一个webapp当中&#xff0c;只要在同一个应用当中&#xff0c;所有的Servlet对象都是共享同一个ServletContext对象的。 ServletContext对象在服务器启动阶段…...

Dockerfile部署SpringBoot项目

Dockerfile部署SpringBoot项目 文章目录 利用Dockerfile部署SpringBoot项目 1、创建一个SpringBooot项目并且打成jar包2、在Linux中创建一个文件夹&#xff0c;来做docker测试3、将jar包上传到Linux中4、编写Dockerfile文件5、制作镜像6、启动容器7、查看容器启动日志8、访问接…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...