如何调用修改Go内存的C函数?代码安全性及Pinner必要性探讨
Go与C交互的GC安全性问题解答
问题背景
假设有如下C函数签名:
// myclib.h void modify(double* ptr, int N);
该函数会修改指针ptr指向的内存区域。
现有如下Go代码,请问这段代码在Go垃圾回收机制下是否安全?是否需要使用runtime.Pinner?
package main // #cgo CFLAGS: -g -Wall // #include "myclib.h" import "C" import ( "fmt" "runtime" ) func modifyWrapper(v []float64) { ptr := (*C.double)(&v[0]) N := (C.int)(len(v)) pinner := runtime.Pinner{} pinner.Pin(ptr) C.modify(ptr, N) pinner.Unpin() } func main() { v := []float64{9.0, 2.0, 1.0, 4.0, 5.0} modifyWrapper(v) }
解答
代码安全性分析
这段代码在Go GC机制下是安全的,但代码中使用runtime.Pinner的操作完全是多余的。
为什么不需要runtime.Pinner?
- 在
modifyWrapper函数执行期间,切片v作为函数参数被当前Go栈引用,Go的垃圾回收器会追踪到这个引用,不会在函数执行过程中回收或移动切片底层的数组内存。 C.modify是同步调用,整个执行过程都处于modifyWrapper函数的生命周期内,此时切片v的内存始终处于Go代码的引用保护下,不存在被GC干扰的风险。
什么时候才需要用runtime.Pinner?
只有当C代码需要长期持有Go对象的指针(比如把指针保存到全局变量,后续异步回调或其他线程中使用),且Go代码中不再持有该对象的任何引用时,才需要用Pinner固定内存,防止GC回收或移动它。这个场景显然不符合当前例子的情况。
内容的提问来源于stack exchange,提问作者Ronaldinho
相关产品推荐
相关产品推荐

