Go weak包前瞻:弱指针为内存管理带来新选择

news2024/11/24 20:27:49

在介绍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的核心工作原理,包括间接对象的使用和垃圾回收时的行为:

06dcb767757af0d4e1067e52bc9d44e8.png

简单看一下这张图:程序创建一个对象并通过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) V
    m 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中似乎少了第二个参数vf
    if !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的快乐! 欢迎大家踊跃加入!

66432d0ea8e7a861debab90b05f55ffa.jpeg8a51067c8f05f2a1a23f625edd1e9a54.png

9659117770fbfcc114dc3b0371decae7.png9d1ccef2ba8b6b04630f86bc9a920d50.jpeg

著名云主机服务厂商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

5b2d209455bdd98fe13532e3c189493b.jpeg

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

参考资料

[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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法分享——《滑动窗口》

🚍《长度最小的子数组》 🚲题目描述: 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符…

Linux进程状态与进程优先级

目录 Linux进程状态与进程优先级 前置知识 并行与并发 时间片 进程状态 基本介绍 等待状态的本质 swap分区 Linux进程状态 Linux进程状态分类 运行状态(Running)与等待状态(Sleeping) 硬盘等待状态(Disk S…

计算机视觉必备模型YOLO系列模型的知识点,提供YOLOv1-v8模型结构与代码实例

大家好,我是微学AI,今天给大家介绍一下计算机视觉必备模型YOLO系列模型的知识点,提供YOLOv1-v8模型结构与代码实例。本文全面介绍了计算机视觉领域中必备的YOLO系列模型,详细梳理了YOLOv1至YOLOv8模型的结构及其演变过程。文章内容…

Node.js 学习 path模块、fs模块、npm软件包管理器、导出、导入

目录 1.Node.js入门 1.1 什么是 Node.js 1.2 fs模块-读写文件 1.3 path模块-路径处理 1.4 案例-压缩前端html 1.5 认识URL中的端口号 1.6 http模块-创建Web服务 1.7 案例-浏览时钟 2.Node.js 模块化 2.1 模块化简介 2.1.1 什么是模块化? 2.1.2 CommonJS…

【软件建设方案】设备管理系统建设方案(Doc原件参考)

1.系统概述 1.1.需求描述 1.2.需求分析 1.3.重难点分析 1.4.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 3.系统功能设计 3.1.功能清单列表 3.2.设备信息数据库 3.3.设备维护计划管理子系统 3.4.设备维护管理子系统 3.5.备件物资管理子系统 3.6.…

1区IEEE-Trans发文暴涨3倍,CCF-B类,刚跌出了TOP榜!这是不是官方提前发出警告?

【SciencePub学术】今天给大家推荐的是1本地球科学领域的SCI—《IEEE TRANSACTIONS ON GEOSCIENCE AND REMOTE SENSING》,IEEE-Trans系列,并且是CCF-B类期刊!此系列的期刊在业界的权威性还是不容置疑的。 优点VS缺点 ✦ IEEE-Trans系列 ✦ C…

普通本科生也能成为AI高手:人工智能学习指南

在人工智能(AI)日益普及的今天,许多人都渴望掌握这项技术,但常有人疑惑:没有顶尖学府的背景,我也能学习人工智能吗? 答案是肯定的! 人工智能是一个充满机遇与挑战的领域&#xff0c…

渗透测试工具 sqlmap 基础教程

一、引言 在网络安全领域,渗透测试是一项至关重要的工作,它可以帮助我们发现系统中的安全漏洞,从而采取相应的措施进行修复。而 sqlmap 作为一款强大的开源渗透测试工具,专门用于检测和利用 SQL 注入漏洞。本文将为大家详细介绍 …

HTTP 1.0 2.0 3.0详解

HTTP HTTP全称超文本传输协议,是一种属于应用层的通信协议。它允许将超文本标记语言文档(HTML)从Web服务器传输到客户端的浏览器。 HTTP报文结构 请求报文结构 请求方法: GET:一般用来请求已被URI识别的资源&#x…

Elasticsearch7.7.1集群不能相互发现的问题解决以及Elasticsearch7.7.1安装analysis-ik中文分词插件的应用

一、Elasticsearch7.7.1集群不能相互发现的问题解决 在使用elasticsearch7.7.1搭建集群,使用了3台服务器作为节点,但在搭建的过程中发现每台服务器的elasticsearch服务都正常,但是不能相互发现,期间进行了一些配置的修改偶尔出现了…

(附源码)SSM养老院综合服务管理系统-计算机毕设 23237

基于SSM的养老院综合服务管理系统 摘 要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,…

助力解析化学图像生成文本分析,化学大语言模型 ChemVLM 来啦!

ChemVLM 是由上海人工智能实验室于 2024 年推出的首个面向化学领域的开源多模态大型语言模型。该模型旨在解决化学图像理解与文本分析之间的不兼容问题,通过结合视觉 Transformer (ViT)、多层感知机 (MLP) 和大型语言模型 (LLM) 的优势,实现了对化学图像…

万维组态介绍

演示地址:http://121.40.16.189:12000 万维组态本地部署文档万维组态线上部署文档万维组态操作说明文档万维组态接入文档万维组态绑点示例文档万维组态接入源代码说明万维组态扩展图元示例文档万维组态大屏图元示例文档 项目介绍 万维组态是一款功能强大的基于Web的…

sar信号RD域的距离向傅里叶变换

下面可知,举例傅里叶变换时,posp 距离时间和频率 t不等于ft/K。而方位时间和频率时这种线性关系

整合SpringSecurity框架经典报错

报错描述Description: Field userDetailsService in com.atguigu.security.config.WebSecurityConfig required a bean of type org.springframe 这是整合SpringSecurity权限认证中经常出现的一个问题,由于SpringSecurity中这个UserDetailsService未找到 解决方案…

稀疏线性方程组求解技术——超节点法(Supernodal)简介

一、介绍 直接法的基础是矩阵的分解,常见的分解形式有LU分解、Cholesky分解、LDL分解等。 直接法通过将A矩阵分解成两个或多个因子的乘积,使得原方程组转化为若干个较容易求解的子问题。例如LU分解ALU,其中L是单位下三角矩阵,U是…

JavaScript typeof运算符

在js中可以typeof来做到确定一个值到底是什么类型。 <script>var num 100;//数值类型var name "mingzi";//字符串类型var book true;//布尔类型var student {name: " 小明",age: 16,tnum: "213444"}//对象是由多个数据组合而成&#x…

效率工具推荐 | 高效管理客服中心知识库

人工智能AI的广泛应用&#xff0c;令AI知识库管理已成为优化客服中心运营的核心策略之一。一个高效、易用且持续更新的知识库不仅能显著提升客服代表的工作效率&#xff0c;还能极大提升客户的服务体验。而高效效率工具如HelpLook&#xff0c;能够轻松搭建AI客服帮助中心&#…

[论文速读] Multimodal Fusion on Low-quality Data:A Comprehensive Survey 低质多模态数据融合综述

摘要&#xff1a; 多模态融合侧重于整合多种模态的信息&#xff0c;以实现更准确的预测&#xff0c;在自动驾驶和医疗诊断等广泛场景中取得了显着进展。然而&#xff0c;多模态融合的可靠性在很大程度上仍未得到探索&#xff0c;特别是在低质量数据设置下。本文调查了野外多模态…

Android平台使用VIA创建语音交互应用

Android平台使用VIA创建语音交互应用 概述 在 Android 平台上开发一款语音助手应用需要整合多种技术,包括语音识别(ASR)、文字转语音(TTS)、以及热词检测(Hotword Detection)。这些技术共同构成了语音助手应用的核心交互方式,使用户能够通过语音命令与设备进行无缝交…