Go weak包前瞻:弱指针为内存管理带来新选择
在介绍Go 1.23引入的unique包的《Go unique包:突破字符串局限的通用值Interning技术实现》一文中,我们知道了unique包底层是基于internal/weak包实现的,internal/weak是一个弱指针功能的Go实现。所谓弱指针(Weak Pointer,也称为弱引用)是与强指针相对而言的,强指针(Strong Pointer,也可称作强引用)就是下面代码片段中的这种常规指针:
var p *T = new(T) // 假设T类型对象被分配到堆上 只要p指向堆上的T对象,那么T对象就无法被GC回收。但弱指针并非如此,它也可以指向堆上的某个内存对象(比如T类型对象),但它无法像强指针那样阻止GC回收该对象。
Go unique包的实现者Michael Knyszek近期提议在标准库引入weak包[3](实际上是将internal/weak公开暴露给Go开发者),该提议被Russ Cox代表的Go提案评审委员会所接受,最早将于Go 1.24版本落地。
在这篇短文中,我们来前瞻一下weak包的API设计、原理、应用场景以及社区对该提案一些观点。
注:weak包尚未落地,本文中的代码在Go 1.23中均无法运行,可以视作伪代码。
1. weak包的API
weak包的核心是Pointer[T]类型,它代表了对类型T的弱指针。以下目前Michael Knyszek为weak包设计的主要API:
type Pointer[T any] struct { ... }func Make[T any](ptr *T "T any") Pointer[T]func (p Pointer[T]) Value() *T Make函数用于创建一个弱指针,而Value方法则用于获取弱指针指向的实际值。如果原始对象已被垃圾回收,Value方法将返回nil。这个设计秉承了Go一贯的简洁,允许开发者轻松创建和使用弱指针,同时保持了Go语言的类型安全特性。
2. weak包弱指针的工作原理
在开篇时,我已经对弱指针的作用做了简单说明,这里结合上述weak包的API和提案中的设计原理再扩展一下。
弱指针的核心思想是允许引用内存而不阻止垃圾回收器回收它。垃圾回收器在回收对象时,会自动将所有指向该对象的弱指针设置为nil。这确保了弱指针不会产生悬空引用(dangling pointer)。
下图是weak包弱指针的工作原理示意图,展示了weak pointer的核心工作原理,包括间接对象的使用和垃圾回收时的行为:

简单看一下这张图:程序创建一个对象并通过weak.Make创建一个weak.Pointer(弱指针),在Go运行时内部,weak.Pointer通过8字节的间接对象引用原始对象。这个间接对象是weak.Pointer的内部字段,按当前internal/weak的实现来看,该字段是一个unsafe.Pointer。这个间接对象包含了实际的弱引用。
值得注意的是,弱指针的比较基于它们最初创建时使用的指针。即使原始对象被回收,两个由相同指针创建的弱指针仍然会被认为是相等的。这个特性使得弱指针可以安全地用作map的键。
3. weak包的典型使用场景
weak包的引入将为Go带来更灵活的内存管理机制,它允许开发者创建不会阻止垃圾回收的引用,从而在保持内存效率的同时,实现更复杂的数据结构和算法。特别是在处理缓存、规范化映射(Canonicalization mapping)[4]等场景时。
以缓存为例,使用弱指针,我们可以创建不会阻止被缓存对象被垃圾回收的缓存系统,这对于管理内存敏感的大型缓存系统特别有用。下面提案中Russ Cox举的一个使用weak包实现简单缓存的示例(可理解为伪代码):
type Cache[K any, V any] struct {f func(*K) Vm atomic.Map[uintptr, func() V]
}func NewCache[K comparable, V any](f func(*K "K comparable, V any")V) *Cache[K, V] {return &Cache[K, V]{f: f}
}func (c *Cache[K, V]) Get(k *K) V {kw := uintptr(unsafe.Pointer((k))vf, ok := c.m.Load(kw)if ok {return vf()}vf = sync.OnceValue(func() V { return c.f(k) })vf, loaded := c.m.LoadOrStore(kw, vf) // 原issue中似乎少了第二个参数vfif !loaded {// Stored kw→vf to c.m; add the cleanup.runtime.AddCleanup(k, c.cleanup, kw)}return vf()
}func (c *Cache[K, V]) cleanup(kw uintptr) {c.m.Delete(kw)
}var cached = NewCache(expensiveComputation) 这段代码定义了一个泛型缓存结构Cache,它有两个类型参数K和V,以及两个成员字段f和m:
f是一个函数,接受*K类型的指针,返回V类型的值,这是用于计算缓存值的函数。
m是一个原子映射,键是K类型的弱指针,值是返回V的函数。
NewCache是缓存的创建函数,接受一个计算函数f,返回初始化的Cache指针。
Cache类型的Get方法用于获取缓存的值,它首先创建键k的弱指针kw,然后以该弱指针为键尝试从缓存(atomicMap)中加载值。如果找到,直接返回缓存的值。如果未找到,使用sync.OnceValue创建一个只执行一次的函数,调用c.f(k)计算值。之后,尝试将新计算的函数存储到缓存中。如果成功存储(即之前没有这个键),添加一个清理函数,最后返回计算后的Value值。
这个实现允许缓存中的键在不再被程序其他部分引用时被垃圾回收,从而避免了内存长期占用或是泄漏。
4. 社区声音
针对该weak包提案,Go社区的主要声音是支持的,认为weak包将为Go带来更灵活的内存管理机制,但也表示了对无法用好weak包这个低级机制的担忧,希望在正式文档或Go Tour中包含更多使用关于weak包的示例和最佳实践。
Go新版GC的主要设计者Richard L. Hudson[5]提出了对sweeping storms和清理大型缓存中过时weak条目的担忧,并提出了使用ephemerons[6](一种更复杂的弱引用机制)的可能性,但也认识到其实现复杂度和性能开销较高。
也有一些Go社区开发者保持了对weak包的谨慎态度,比如fasthttp的维护者、VictorialMetrics[8]的联创Aliaksandr Valialkin[9] 就建议:在决定如何在Go中实现弱指针之前,最好先分析其他编程语言中弱指针的最常见的生产用例,并首先思考一下在标准库中为这些实际用例提供更高级别的解决方案而不是暴露较低级别的弱指针的方案是否会更好。
也有gopher提出:能否在提案中添加2-3个没有弱指针就无法解决的实际问题的例子,但Michael Knyszek并未回应。
5. 小结
weak包的引入让Go的工具箱更加完整,它为开发者提供了更细粒度的内存控制,同时其核心API也保持了Go简单易用的特性。
对于Go开发者来说,weak包使得某些复杂的内存管理场景变得更容易处理,但也需要开发者更好地理解垃圾回收机制和弱引用的工作原理。
社区对weak包的引入持积极态度,但也关注其实现细节、性能影响和最佳实践,同时也意识到了使用weak指针时可能面临的挑战。
不过,开发者在使用weak包时还是需要谨慎,毕竟过度使用弱指针可能会使代码变得难以理解和维护,最好的方法是将它用在最适合的场景下。
往期推荐
- Go unique包:突破字符串局限的通用值Interning技术实现
- Go 1.21中值得关注的几个变化
- Go GC如何检测内存对象中是否包含指针
- htmx:Gopher走向全栈的完美搭档?
- Go语言的“黑暗角落”:盘点学习Go语言时遇到的那些陷阱
Gopher部落知识星球[10]在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!




著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址[11]:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。
Gopher Daily(Gopher每日新闻) - https://gopherdaily.tonybai.com
我的联系方式:
微博(暂不可用):https://weibo.com/bigwhite20xx
微博2:https://weibo.com/u/6484441286
博客:tonybai.com
github: https://github.com/bigwhite
Gopher Daily归档 - https://github.com/bigwhite/gopherdaily
Gopher Daily Feed订阅 - https://gopherdaily.tonybai.com/feed
商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。
参考资料
[1]
Go 1.23: https://tonybai.com/2024/08/19/some-changes-in-go-1-23
[2]Go unique包:突破字符串局限的通用值Interning技术实现: https://tonybai.com/2024/09/18/understand-go-unique-package-by-example/
[3]Michael Knyszek近期提议在标准库引入weak包: https://github.com/golang/go/issues/67552
[4]规范化映射(Canonicalization mapping): https://wiki.c2.com/?CanonicalizedMapping
[5]Richard L. Hudson: https://github.com/RLH
[6]ephemerons: https://dl.acm.org/doi/pdf/10.1145/263698.263733
[7]fasthttp: https://tonybai.com/2021/04/25/server-side-performance-nethttp-vs-fasthttp
[8]VictorialMetrics: https://github.com/VictoriaMetrics/VictoriaMetrics
[9]Aliaksandr Valialkin: https://github.com/valyala
[10]Gopher部落知识星球: https://public.zsxq.com/groups/51284458844544
[11]链接地址: https://m.do.co/c/bff6eed92687
相关文章:
Go weak包前瞻:弱指针为内存管理带来新选择
在介绍Go 1.23引入的unique包的《Go unique包:突破字符串局限的通用值Interning技术实现》一文中,我们知道了unique包底层是基于internal/weak包实现的,internal/weak是一个弱指针功能的Go实现。所谓弱指针(Weak Pointer,也称为弱…...
ZStack AIOS平台智塔入选2024世界计算大会专题展优秀成果
9月24日至25日,由湖南省人民政府主办,湖南省工业和信息化厅、长沙市人民政府、中国电子信息产业发展研究院承办的2024世界计算大会在湖南长沙举办。云轴科技ZStack AIOS平台智塔凭借在智算领域的卓越表现,入选2024世界计算大会专题展优秀成果…...
总结 自行解决问题经验
一、总结在使用Linux时遇到的各种坑 yum 源要替换为国内源wget 需要用yum先行下载在make的时候需要预先安装各种库端口无法访问时要记得去防火墙开启端口访问权限安装完各种程序的时候记得创建环境变量或者软链接… 二、遇到故障如何正确高效的去解决 在使用yum下载wget的时…...
软件设计模式——工厂模式
软件设计模式——工厂模式 文章目录 软件设计模式——工厂模式一、设计模式的认知1.1 什么是软件设计模式:1.2 为什么要学习设计模式:1.3 设计模式的分类: 二、工厂模式2.1 工厂模式实例: 一、设计模式的认知 1.1 什么是软件设计…...
Apache Druid命令执行(CVE-2021-25646)
漏洞详情: Apache Druid 是用Java编写的面向列的开源分布式数据存储系统,旨在快速获取大量事件数据,并在数据之上提供低延迟查询。 Apache Druid含有能够执行嵌入在各种类型请求中由用户提供的JavaScript代码功能。此功能适用于高度信任环境…...
新的 MathWorks 硬件支持包支持从 MATLAB 和 Simulink 模型到高通 Hexagon 神经处理单元架构的自动化代码生成
MathWorks 今天宣布,推出针对 Qualcomm Hexagon™ 神经处理单元(NPU)的硬件支持包。该处理单元嵌入在 Snapdragon 系列处理器中。MathWorks 硬件支持包,则专门针对 Qualcomm Technologies 的 Hexagon NPU 架构进行优化,…...
gorm.io/sharding:改造,当查询条件中不包含分表键时,从自定义方法中获取对应的表进行查询
项目背景 这篇文章是一种特殊的情形——当查询条件中不包含分表键时,从自定义方法中获取对应的表进行查询。实际项目中并不建议这种用法。 当然,这里只是提供一种思路。这篇文章也是这个系列中的第三篇文章。前两篇文章《基于gorm.io/sharding分表中间…...
【CSS】变量的声明与使用
原生变量root 伪类 原生变量 CSS中我们可以统一设置变量方便页面维护。变量声明的时候,自定义样式变量名之前加上两根连词线 " – " 即可,使用 var() 来引用。声明的变量是有作用域的 ( 比如是在html中声明的变量,那么该变量在html…...
【数学分析笔记】第3章第4节闭区间上的连续函数(1)
3. 函数极限与连续函数 3.4 闭区间上的连续函数 3.4.1 有界性定理 【定理3.4.1】 f ( x ) f(x) f(x)在闭区间 [ a , b ] [a,b] [a,b]上连续,则 f ( x ) f(x) f(x)在闭区间 [ a , b ] [a,b] [a,b]上有界。 【证】用反证法,假设 f ( x ) f(x) f(x)在 [ …...
Mybatis中sql数组为空判断
一、Mybatis xml中的sql通过if语句判定是否为空 <if test"arrays ! null"> </if>上述示例只能判断arrays数组不为null,那如果是个空数组呢 二、Mybatis xml中的sql通过if语句判定数组非空数组 <if test"arrays ! null and arrays.l…...
12.第二阶段x86游戏实战2-CE找基地址
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要…...
笔记整理—内核!启动!—linux应用编程、网络编程部分(3)文件共享与标准IO
文件共享是指同一个文件被多个独立的读写进行操作。同一个文件为同一个inode,同一个pathname也就是同一个静态文件。同时进行操作,打开一个文件未关闭又被另一个操作打开。文件共享的方式可以实现高效的大文件读写。 文件共享的三种方式:①同…...
plt常用函数介绍一
目录 前言plt.figure()plt.subplot()plt.subplots()plt.xticks()plt.xlim() 前言 Matplotlib是Python中的一个库,它是数字的-NumPy库的数学扩展。 Pyplot是Matplotlib模块的基于状态的接口。在Pyplot中可以使用各种图,例如线图,轮廓图&#…...
基于ExtendSim的 电子制造 仿真模型
说明: 此模型表示电路板制造设施。该过程有4个步骤: *焊料制备 *组件放置 *烤箱 *检查 详情: *烤箱的容量为10张卡,但如果烤箱循环开始时仅能处理5张卡,则最多只能处理5张。 *如果检查员发现问题,他们将修理…...
BGP 路由反射器
转载:BGP 路由反射器 / 实验介绍: / 原理概述 缺省情况下,路由器从它的一个 IBGP 对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体,这个原则称为BGP水平分割 原则,该原则的根本作用是防止 AS 内部的 BGP 路由…...
CSRF高级防御绕过
1)回顾low级别做过csrf页面的密码重置,重复之前的操作,我们发现级别调整中级之后,报错如下 2)检查源码 进入dvwa源码,查找到checktoken: 3)在dvwa-csrf页面上,抓包 http…...
MySQL安装文档-Windows
文章目录 MySQL安装1. 安装2. 配置 MySQL安装 1. 安装 1). 双击官方下来的安装包文件 2). 根据安装提示进行安装 安装MySQL的相关组件,这个过程可能需要耗时几分钟,耐心等待。 输入MySQL中root用户的密码,一定记得记住该密码 2. 配置 安装好MySQL之后…...
html TAB、table生成
1. 代码 <!DOCTYPE html> <head> <meta charset"UTF-8"> <title>Dynamic Tabs with Table Data</title> <style> /* 简单的样式 */ .tab-content { display: none; border: 10px solid #ccc; padding: 30px; mar…...
2024!再见前端!
各位朋友大家晚上好,夜深了,睡不着,想想还是写一篇文章和大家说再见吧! 自2014年入行前端以来,满打满算差不多整整十年了,这十年可以说是见证了中国整个互联网的起飞到全盛时期。这期间经历了电商、金融、…...
【源码+文档+调试讲解】人事管理系统设计与实现Python
摘 要 人事管理系统的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品,体验高科技时代带给人们的方便,同时也能让用户体会到与以往常规产品不同的体验风格。 与安卓,iOS相比较起来ÿ…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
