这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!
您需要 登录 才可以下载或查看,没有账号?立即注册
×
在 Go 语言中,“弱指针”是一种不会阻止垃圾回收器(GC)回收目标对象的引用。当一个对象只剩下弱指针引用,而没有任何强引用时,GC 会将该对象标记为不可达并回收。此后,所有指向该对象的弱指针会自动变为 nil。 简而言之:弱指针不会影响对象的生命周期,因此在使用前必须检查其是否为 nil。
Go 1.24 新增 weak 包
Go 1.24 正式引入了标准库中的 weak 包,提供了简洁的 API 来创建和访问弱指针。 - import "weak"
- type MyStruct struct {
- Data string
- }
- func main() {
- obj := &MyStruct{Data: "example"}
- wp := weak.Make(obj) // 创建弱指针
- val := wp.Value() // 获取强引用或 nil
- if val != nil {
- fmt.Println(val.Data)
- } else {
- fmt.Println("对象已被垃圾回收")
- }
- }
复制代码weak.Make(obj) 创建了一个指向 obj 的弱指针。调用 wp.Value() 时,如果对象仍然存活,则返回一个强引用;否则返回 nil。
测试弱指针行为
我们可以通过移除强引用并主动触发 GC 来观察弱指针的行为: - import (
- "fmt"
- "runtime"
- "weak"
- )
- type MyStruct struct {
- Data string
- }
- func main() {
- obj := &MyStruct{Data: "test"}
- wp := weak.Make(obj)
- obj = nil // 移除强引用
- runtime.GC() // 触发 GC
- if wp.Value() == nil {
- fmt.Println("对象已被垃圾回收")
- } else {
- fmt.Println("对象仍然存活")
- }
- }
复制代码
强引用 vs 弱引用
特性 | 强引用 (*T) | 弱引用 (weak.Pointer[T]) | 是否阻止 GC | 是 | 否 | 空值 | nil | nil(被回收或未赋值) | 访问方式 | 直接使用 | 调用 Value() 获取强引用 |
示例 1:弱指针实现轻量级缓存使用弱指针最典型的场景之一是缓存:在不强制条目常驻内存的情况下临时保存数据。 - package main
- import (
- "fmt"
- "runtime"
- "sync"
- "weak"
- )
- type User struct {
- Name string
- }
- var cache sync.Map // map[int]weak.Pointer[*User]
- func GetUser(id int) *User {
- if wp, ok := cache.Load(id); ok {
- if u := wp.(weak.Pointer[User]).Value(); u != nil {
- fmt.Println("cache hit")
- return u
- }
- }
- u := &User{Name: fmt.Sprintf("user-%d", id)}
- cache.Store(id, weak.Make(u))
- fmt.Println("load from DB")
- return u
- }
- func main() {
- u := GetUser(1) // 第一次加载
- fmt.Println(u.Name)
- runtime.GC() // 即使 GC,因 main 持有强引用,User 仍在
- u = nil // 移除强引用
- runtime.GC() // 再次 GC,User 可能被回收
- _ = GetUser(1) // 若被回收,会重新加载
- }
复制代码输出示例: - load from DB
- user-1
- load from DB
复制代码
为什么使用弱指针?适用场景包括:
使用弱指针的注意事项始终检查是否为 nil:GC 可在任意时间回收对象; 避免形成循环依赖:否则弱引用会变成实际的强引用链; 性能权衡:频繁使用 Value() 访问弱引用可能影响性能,尤其在短时间内频繁创建、销毁对象的场景中。
示例 2:强引用的普通用法
- package main
- import (
- "fmt"
- "runtime"
- )
- type Session struct {
- ID string
- }
- func main() {
- s := new(Session) // 与 &Session{} 等价
- s.ID = "abc123"
- fmt.Println("strong ref alive:", s.ID)
- s = nil // 移除强引用
- runtime.GC() // 手动触发 GC(仅建议)
- fmt.Println("done")
- }
复制代码 只要变量 s 仍然存在,GC 就不会回收 Session 对象。只有当它不再从任何根对象(栈、全局变量等)可达时,才会被释放。
回收机制:强指针何时释放?Go 的 GC 使用可达性分析(Mark and Sweep): 简而言之:只要有强引用链存在,对象就不会被 GC 回收;一旦断开,下一轮 GC 就可能释放它。
总结Go 1.24 中引入的 weak 包,提供了一个原生、安全的方式实现弱引用。它尤其适用于缓存、资源观察、依赖图等场景,可帮助我们更好地控制内存使用,并避免不必要的内存泄漏。 当你希望对象在“用完即走”的同时还能保留轻量级访问能力,弱指针是你的理想选择。 |