最近 Go 又有了新的动作,一路绿灯,已经审批通过了新增弱指针(weak pointers)库的提案,这对于 Go 来讲是一个不错的补充。
今天这篇文章我们主要分享弱指针的介绍,让大家有一个基本前瞻。因为这个提案已经 Accepted!
弱指针是什么
此处是引用 Go 官方提案给出的介绍和原因,也就是为什么 Go 核心团队认可要去做弱指针。
弱指针(或其他语言中的弱引用)允许开发人员引用内存,而不妨碍垃圾回收器回收内存。为防止出现可见的悬挂引用,弱指针在引用的内存被回收时会变为零。
弱指针可以转换为常规(“强”)指针,这样就能防止垃圾回收器回收内存,并允许对内存进行典型使用和访问。
弱指针通常比普通指针更难处理,因为它们随时都可能变为零。几乎每一次弱指针到强指针的转换都必须进行 nil 检查。通常,弱指针会在意想不到的时候变为零。
尽管如此,弱指针仍然存在于许多语言中,因为它们非常有用。弱指针的主要使用场景与高效内存管理和回收有关。
可能的场景包含但不限于:
为规范化映射有效管理内存,或为生命周期与另一个对象的生命周期绑定的内存(类似于 JavaScript 的 WeakMap)有效管理内存。 弱指针的另一个良好用例是向 GC 提示:可以放弃某些资源,因为以后重建这些资源的成本很低,尤其是在这些资源占用大量内存的情况下。
弱指针 API 设计
Go 官方计划新增一个 weak 包,添加以下 API 作为弱指针的使用:
type Pointer[T any] struct { ... }
func Make[T any](ptr *T "T any") Pointer[T] { ... }
func (p Pointer[T]) Value() *T { ... }
这些 API 主要用于创建和管理弱指针。Pointer[T]
是一个弱指针类型,能够引用类型为 T 的值,但不会阻止该值被垃圾回收。
使用 Make
函数可以从一个有效的指针中创建一个弱指针,而 Value
方法则返回原始指针,若值已被回收则返回 nil。
弱指针的比较遵循特定规则,例如:同一对象的不同字段创建的弱指针不相等,且如果对象通过 runtime.SetFinalizer 复活,之前的弱指针也会失效。
这样的设计目的是为了有效管理内存并避免内存泄漏。
使用例子
前 Go 核心团队负责人 rsc 在接纳这个提案时,设计了一个弱缓存抽象的例子。
代码如下:
type Cache[K any, V any] struct {
f func(*K) V
m atomic.Map[weak.Pointer[K], 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 := weak.Make(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)
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 weak.Pointer[K]) {
c.m.Delete(kw)
}
var cached = NewCache(expensiveComputation)
部分社区争议
争议主要集中在以下几个方面:
必要性:部分开发者质疑弱指针是否解决了实际问题,是否足够常用。 复杂性:有人担心引入弱指针会增加代码复杂性,尤其对新手开发者。 性能开销:讨论弱指针可能带来的性能影响,是否值得引入。 兼容性:考虑弱指针与现有内存管理机制的兼容性。 接口设计:对弱指针的具体实现和接口设计存在不同意见。
总的来讲,围绕着弱指针的实际需求、使用复杂性及其对性能的影响。还有如何设计其接口等问题展开。
注:问题都是问题。rsc 已经以一己之力把这个提案往 Accepted 推了。
总结
后续弱指针的引用,可以给 Go 的使用打开一个新的 “后门”。随着这个趋势的不断形成,个人觉得以后 Go 也能有一派开发者针对自己的高性能要求的代码场景玩出一波骚操作了。
推荐阅读
看了这么多Go文章,知识点学了又学,自己还是不能独立Lead项目?
推荐一下我的新专栏《Go项目搭建和整洁开发实战》,本专栏力主实战技能,配备一个完整的实战项目,扫下方二维码可查看已更新章节
订阅专栏后,可加入专栏配套的实战项目,获得完整实战教程,同时也有专属的读者群,欢迎加入一起学习。
专栏分为五大部分,目前已经更新20多节教程,主要内容架构如下:
第一部分介绍让框架变得好用的诸多实战技巧,比如通过自定义日志门面让项目日志更简单易用、支持自动记录请求的追踪信息和程序位置信息、通过自定义Error在实现Go error接口的同时支持给给错误添加错误链,方便追溯错误源头。 第二部分:讲解项目分层架构的设计和划分业务模块的方法和标准,让你以后无论遇到什么项目都能按这套标准自己划分出模块和逻辑分层。后面几个部分均是该部分所讲内容的实践。 第三部分:设计实现一个套支持多平台登录,Token泄露检测、同平台多设备登录互踢功能的用户认证体系,这套用户认证体系既可以在你未来开发产品时直接应用 第四部分:商城app C端接口功能的实现,强化分层架构实现的讲解,这里还会讲解用责任链、策略和模版等设计模式去解决订单结算促销、支付方式支付场景等多种多样的实际问题。 第五部分:单元测试、项目Docker镜像、K8s部署和服务保障相关的一些基础内容和注意事项
扫描上方的海报二维码或者访问 https://xiaobot.net/p/golang 即刻订阅
点击阅读原文可跳转。