【Go语言的垃圾回收机制(3)】

简介: 【Go语言的垃圾回收机制(3)】

并行的垃圾回收


Go语言在这个版本中不仅实现了精确的垃圾回收,而且实现了并行的垃圾回收。标记算法本质上就是一个树的遍历过程,上面实现的是一个递归版本。

并行的垃圾回收需要做的


1.第一步和🐱

就是先将算法做成非递归的。非递归版本的树的遍历需要用到一个队列。


根结点进队

while(队列不空){

出队

访问

将子结点进队

}


2.第二步🦁

使上面的代码能够并行地工作,显然这时是需要一个线程安全的队列的。假设有这样一个队列,那么上面代码就能够工作了。但是,如果不加任何优化,这里的队列的并行访问非常地频繁,对这个队列加锁代价会非常高,即使是使用 CAS 操作也会大大降低效率。


3.第三步🐯

要做的就是优化上面队列的数据结构。事实上,Go中并没有使用这样一个队列,为了优化,它通过三个数据结构共同来完成这个队列的功能,这三个数据结构分别是 PtrTarget 数组,Workbuf,lfstack。


Workbuf🦒


Workbuf 听名字就知道,这个结构体的意思是工作缓冲区,里面存放的是一个数组,数组中的每个元素都是一个待处理的结点,也就是一个 Obj 指针。这个对象本身是已经标记了的,这个对象直接或间接引用到的对象,都是应该被标记的,它们不会被当作垃圾回收掉。Workbuf 是比较大的,一般是 N 个内存页的大小(目前是 2 页,也就是 8K)。


PtrTarget 数组🦊


PtrTarget 数组也是一个缓冲区,相当于一个 intermediate buffer,跟 Workbuf 有一点点的区别。

第一,它比 Workbuf 小很多,大概只有 32 或 64 个元素的数组。

第二,Workbuf 中的对象全部是已经标记过的,而 PtrTarget 中的元素可能是标记的,也可能是没标记的。

第三,PtrTarget 里面的元素是指针而不是对象,指针是指向任意地址的,而对象是对齐到正确地址的。


lfstack🦝


另一个数据结构是 lfstack,这个名字的意思是 lock free 栈。其实它是被用作了一个无锁的链表,链表结点是以 Workbuf 为单位的。并行垃圾回收中,多条线程会从这个链表中取数据,每次以一个 Workbuf 为工作单位。


同时,标记的过程中也会产生 Workbuf 结点放到链中。lfstack 保证了对这个链的并发访问的安全性。由于现在链表结点是以 Workbuf 为单位的,所以保证整体的性能,lfstack 的底层代码是用 CAS 操作实现的。


经过第三步中数据结构上的拆解,整个并行垃圾回收的架构已经呼之欲出了,这就是标记扫描的核心函数 scanblock。这个函数是在多线程下并行安全的。


最后一步

,多线程并行。整个的 gc 是以 runtime.gc 函数为入口的,它实际调用的是 gc。进入 gc 函数后会先 stoptheworld,接着添加标记的 root 区域。然后会设置 markroot 和 sweepspan 的并行任务。运行 mark 的任务,扫描块,运行 sweep 的任务,最后 starttheworld 并切换出去。


垃圾回收的时机🐮


垃圾回收的触发是由一个 gcpercent 的变量控制的,当新分配的内存占已在使用中的内存的比例超过 gcprecent 时就会触发。


例:gcpercent=100,当前使用了 4M 的内存,那么当内存分配到达 8M 时就会再次 gc。如果回收完毕后,内存的使用量为 5M,那么下次回收的时机则是内存分配达到 10M 的时候。也就是说,并不是内存分配越多,垃圾回收频率越高,这个算法使得垃圾回收的频率比较稳定,适合应用的场景。


gcpercent 的值是通过环境变量 GOGC 获取的,如果不设置这个环境变量,默认值是 100。如果将它设置成 off,则是关闭垃圾回收。


好啦!今天的垃圾都丢完啦!(给自己一个大大的赞👍吧!)


最后


🦄🦄🦄

想要学好一门新的语言,首先要找到相应的学习资料和学习平台,一个良好的学习平台是必要的,比如🧸🎈🎈🎈牛客网🌼🌼🌼它就非常适合萌新们来学习,在之前的文章里也有介绍到哦!它就像go语言一样简洁,方便,实用。(如果您觉得我的文章有用的话,请动动您那发财的小手点个赞👍呗)

目录
相关文章
|
1月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
152 1
|
3月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
286 1
|
3月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
359 0
|
3月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
232 0
|
3月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
212 0
|
3月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
305 0
|
3月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
9月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
9月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
3月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。