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

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) 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的快乐! 欢迎大家踊跃加入!

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.mzph.cn/pingmian/54718.shtml

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

相关文章

HarmonyOS鸿蒙开发实战(5.0)自定义路由栈管理

鸿蒙HarmonyOS NEXT开发实战往期文章必看(持续更新......) HarmonyOS NEXT应用开发性能实践总结 HarmonyOS NEXT应用开发案例实践总结合集 最新版!“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门…

真实数据,告诉你3S相关专业本硕毕业生就业去向

本期推文将基于2015届-2023届3S相关专业毕业生(包括本硕博所有毕业生)的生源地、性别分布、行业岗位等数据进行分析,为各位同学提供一些参考,希望可以对各位同学的职业规划与有一定的帮助。 GIS开发资料分享https://www.wjx.cn/v…

10.Lab Nine —— file system-上

首先切换分支到fs git checkout fs make clean 预备知识 mkfs程序创建xv6文件系统磁盘映像,并确定文件系统的总块数,这个大小在kernel/param.h中的FSSIZE写明 // kernel/params.h #define FSSIZE 200000 // size of file system in blocks Make…

Redisson分布式锁的概念和使用

Redisson分布式锁的概念和使用 一 简介1.1 什么是分布式锁?1.2 Redisson分布式锁的原理1.3 Redisson分布式锁的优势1.4 Redisson分布式锁的应用场景 二 案例2.1 锁竞争案例2.2 看门狗案例2.3 参考文章 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff…

生活英语口语柯桥学英语“再确认一下“ 说成 “double confirm“?这是错误的!

在追求英语表达的过程中,我们常常会遇到一些看似合理实则错误的表达习惯。今天,我们就来聊聊一个常见的误区——“再确认一下”被误译为“double confirm”。 “再次确认”不是double confirm 首先,我们需要明确,“double confi…

2.1 HuggingFists系统架构(二)

部署架构 上图为HuggingFists的部署架构。从架构图可知,HuggingFists主要分为服务器(Server)、计算节点(Node)以及数据库(Storage)三部分。这三部分可以分别部署在不同的机器上,以满足系统的性能需求。为部署方便,HuggingFists社区版将这三部…

YOLOv9改进,YOLOv9主干网络替换为GhostNetV2(华为提出的轻量化架构)

摘要 摘要:轻量级卷积神经网络(CNN)专为移动设备上的应用而设计,具有更快的推理速度。卷积操作只能在窗口区域内捕捉局部信息,这限制了性能的进一步提升。将自注意力引入卷积可以很好地捕捉全局信息,但会极大地拖累实际速度。本文提出了一种硬件友好的注意力机制(称为 D…

前端文件上传全过程

特别说明:ui框架使用的是蚂蚁的antd 这里主要是学习前端上传接口的传递参数包括前端上传之前对于代码的整理 一、第一步将前端页面画出来 源代码: /** 费用管理 - IT费用管理 - 费用数据上传 */ import { useState } from "react"; import {…

Prometheus篇之利用promtool工具校验配置正确性

promtool工具 promtool是Prometheus的一个命令行工具,它提供了一些功能来帮助用户进行Prometheus配置文件(如prometheus.yml)的检查、规则检查和调试。 解释 promtool check config: 验证Prometheus配置文件的语法和设置。 promtool命令&…

【最基础最直观的排序 —— 选择排序算法】

最基础最直观的排序 —— 选择排序算法 选择排序算法是一种简单直观的排序算法。其基本思想是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小&a…

WebPage-Bootstrap框架(container类,container-fluid类,栅格系统)

1.Bootstrap Bootstrap为页面内容和栅格系统包裹了一个.container容器,框架预先定义类 1.1container类 响应式布局容器的宽度 手机-小于768px 宽度设置100%; 平板-大于等于768px 设置宽度为750px 桌面显示器-大于等于992px 设置宽度 970px 大屏幕显…

【Bug解决】Nacos启动成功,但却无法访问(提示:无法访问此网站,192.168.10.88的响应时间过长)

情形如下:第一次启动运行一些正常,非正常关闭虚拟机,第二次启动虚拟机查看容器状态如下: docker nacos容器一切正常启动,但是就是无法访问web控制面板,访问无法连接。 首先:执行命令查看日志 …

ClickHouse 与 Quickwit 集成实现高效查询

1. 概述 在当今大数据分析领域,ClickHouse 作为一款高性能的列式数据库,以其出色的查询速度和对大规模数据的处理能力,广泛应用于在线分析处理 (OLAP) 场景。ClickHouse 的列式存储和并行计算能力使得它在处理结构化数据查询时极具优势&…

初探shell与bash使用指南

文章目录 一、shell二、bash第一步、新建脚本第二步、添加权限第三步、执行bash脚本 在日常开发中,经常使用到Linux服务器相关知识,输入命令获取想要的结果,本篇介绍shell 与 bash的相关知识。 一、shell 是命令行解释器,接收用户…

深入解析网络通信关键要素:IP 协议、DNS 及相关技术

我的主页:2的n次方_ 1. IP 协议报头结构 4 位版本:表示 IPv4 / IPv6 4 位首部长度:表示 IP 报头的长度,以 4 字节为单位 8 位服务类型:包括 3 位优先权字段(已弃用),4 位 TOS 字…

电路 - 笔记2

1 555 芯片 2 类比 - pU*I 与 Fm*a 是不是可以与牛顿定律类比 - Fm*a 人的力量(F)有限。 当推大箱子(m)时,加速度(a)就不会很大 当推小箱子(m)时,加速度…

分布式框架 - ZooKeeper

一、什么是微服务架构 1、单体架构 顾名思义一个软件系统只部署在一台服务器上。 ​ 在高并发场景中,比如电商项目,单台服务器往往难以支撑短时间内的大量请求,聪明的架构师想出了一个办法提高并发量:一台服务器不够就加一台&am…

球体检测系统源码分享

球体检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

springboot实战学习笔记(5)(用户登录接口的主逻辑)

接着上篇博客学习。上篇博客是已经基本完成用户模块的注册接口的开发以及注册时的参数合法性校验。具体往回看了解的链接如下。 springboot实训学习笔记(4)(Spring Validation参数校验框架、全局异常处理器)-CSDN博客文章浏览阅读576次,点赞7…

Agile Modbus STM32裸机移植 从机使用

本教程手把手教你实现Agile Modbus,照抄就能成。 并且会解读函数功能含义。 1. 引言 Agile Modbus 是一个轻量级的 Modbus 协议栈,可以满足用户在任何场景下的需求。 功能 支持 rtu 和 tcp 协议,使用纯 C 语言开发,不涉及任何硬件接口,可以直接在任何形式的硬件上使用。由…