Go函数可安全返回局部变量指针,因编译器通过逃逸分析将需长期存在的变量自动分配到堆;但高频逃逸会增加GC压力,且跨goroutine共享指针易致竞态或内存泄漏。
是的,func() *int 这类函数可以放心返回局部变量地址,Go 不会像 C 那样产生悬空指针。根本原因在于编译器在编译期做逃逸分析(Escape Analysis),一旦发现变量地址被返回、赋给全局变量、存入 map 或 slice、或传入 interface{},就会把该变量从栈分配改为堆分配。
实操建议:
go build -gcflags="-m -l" 查看逃逸详情,关键提示是 “moved to heap: x” 或 “escapes to heap”
Go 指针本身不管理生命周期,它只是引用路径的一环。只要存在任意一条从根对象(如全局变量、goroutine 栈、正在运行的 channel)出发、能抵达该对象的指针链,GC 就不会回收它。
常见陷阱:
*User 存进包级 var cache = make(map[string]*User) 却忘了清理 → 对象永远无法回收&largeStruct 传给 fmt.Errorf("err: %v", ...) → 整个结构体因接口隐式持有而滞留内存诊断方法:pprof heap 看类

runtime.SetFinalizer 打钩子验证对象是否如期释放(仅用于诊断)。
多个 goroutine 通过同一指针读写同一块内存,既可能造成数据竞争(race),也可能让 GC 误判对象仍被活跃使用——哪怕逻辑上那个 goroutine 已经结束。
安全做法:
channel 发送值(或小结构体)转移所有权sync.Mutex 或 sync.RWMutex 显式保护临界区-race 编译参数,主动暴露读写冲突(如 go run -race main.go)interface{} 的指针会被隐式延长生命周期,尤其在日志、错误包装、中间件等泛化场景中Go 调 C 产生的 *C.xxx 类型指针不受 GC 管理,它的生死完全取决于你是否调用对应 C.free 或 C 层释放逻辑。
必须遵守:
*C.char 直接塞进 Go 的 map、slice 或 chan 中长期持有C.GoBytes 或 unsafe.Slice 复制到 Go 堆内存,交由 GC 管理nil,并确保不再解引用最易被忽略的是:你以为函数返回了,C 内存就安全了——其实只要 Go 侧还存着那个 *C.xxx,它就是悬空的,且无任何运行时检查。