在 Go 语言的发展历程中,内存管理一直是个重要话题。我们有垃圾回收器帮助自动回收内存,但在某些场景下,比如需要管理系统资源(文件描述符、内存映射等)时,就需要更精细的控制。
今天给大家分享的两个在垃圾回收(GC)方面挺有意思的新特性:runtime.AddCleanup清理函数和weak.Pointer弱指针。
这两个功能不仅解决了传统 finalizer 的痛点,还为内存管理和性能优化提供了全新的解决方案。一起来学习吧!
背景
在 Go 语言的发展历程中,内存管理一直是个重要话题。我们有垃圾回收器帮助自动回收内存,但在某些场景下,比如需要管理系统资源(文件描述符、内存映射等)时,就需要更精细的控制。
之前主要依赖runtime.SetFinalizer来实现资源清理,但说实话,finalizer 使用起来真的很容易踩坑。
最大的问题就是对象复活(object resurrection):finalizer 会让原本应该被回收的对象"复活",至少需要两次 GC 周期才能真正回收内存,还容易造成循环引用问题。
runtime.AddCleanup:更好的资源清理方案
Go 团队也意识到了这些问题。
随着 Go 语言的不断演进,推出了更优雅的解决方案:runtime.AddCleanup和weak.Pointer。
核心改进
runtime.AddCleanup的最大改进在于:清理函数不会接收原始对象作为参数。
这个设计直接解决了 finalizer 的两大痛点:
- 避免对象复活问题。
- 支持循环引用的对象清理。
看个实际例子,用内存映射文件来演示:
//go:build unix
package main
import (
"os"
"runtime"
"syscall"
)
type MemoryMappedFile struct {
data []byte
}
func NewMemoryMappedFile(filename string) (*MemoryMappedFile, error) {
f, err := os.Open(filename)
if err != nil {
returnnil, err
}
defer f.Close()
// 获取文件信息,主要是文件大小
fi, err := f.Stat()
if err != nil {
returnnil, err
}
// 提取文件描述符
conn, err := f.SyscallConn()
if err != nil {
returnnil, err
}
var data []byte
connErr := conn.Control(func(fd uintptr) {
// 创建内存映射
data, err = syscall.Mmap(int(fd), 0, int(fi.Size()),
syscall.PROT_READ, syscall.MAP_SHARED)
})
if connErr != nil {
returnnil, connErr
}
if err != nil {

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



