Go生成C共享库后释放C.CString内存时代码不可达问题排查
Go C共享库Python调用时内存释放崩溃问题
我用Go编写C共享库供Python调用,功能已实现,但调用C.free(unsafe.Pointer(result))后,Go与Python后续代码均无法执行,Python在free函数处直接停止。
Go端代码(带日志)
import ( "fmt" "runtime/debug" "sync/atomic" "unsafe" ) // #include <stdbool.h> // #include <stdint.h> // #include <stdio.h> // #include <stdlib.h> import "C" var allocCount int32 //export Test func Test(s *C.char) *C.char { goS := C.GoString(s) res := goS + "!" fmt.Println(res) atomic.AddInt32(&allocCount, 1) return C.CString(res) } //export FreeResult func FreeResult(result *C.char) C.bool { defer func() { if r := recover(); r != nil { fmt.Printf("Recovered in FreeResult: %v\n", r) fmt.Printf("Stack trace:\n%s\n", debug.Stack()) } }() fmt.Printf("Entered FreeResult\n") fmt.Printf("Result: %v\n", result) fmt.Printf("Allocated memory before freeing: %d\n", atomic.LoadInt32(&allocCount)) if result != nil { C.free(unsafe.Pointer(result)) atomic.AddInt32(&allocCount, -1) fmt.Println("Freed result") fmt.Println("Allocated memory after freeing: ", atomic.LoadInt32(&allocCount)) return C.bool(true) } else { fmt.Println("Error: result is nil") return C.bool(false) } } //export GetAllocCount func GetAllocCount() C.int { return C.int(atomic.LoadInt32(&allocCount)) } func main() {}
Python端调用代码
import ctypes library = ctypes.cdll.LoadLibrary('./lib.dll') test = library.Test test.argtypes = [ctypes.c_char_p] test.restype = ctypes.c_char_p free_result = library.FreeResult free_result.argtypes = [ctypes.c_char_p] free_result.restype = ctypes.c_bool get_alloc_count = library.GetAllocCount get_alloc_count.argtypes = [] get_alloc_count.restype = ctypes.c_int res = test(b"Hello") print(f"Result from test: {res}") print(f"Alloc count after test: {get_alloc_count()}") print(f"Pointer value before freeing: {res}") res = free_result(res) print(f"Free result: {res}") print(f"Alloc count after free: {get_alloc_count()}")
编译命令(Windows)
go build -buildmode=c-shared -o lib.dll lib.go
实际运行输出
Hello! Result from test: b'Hello!' Alloc count after test: 1 Pointer value before freeing: b'Hello!' Entered FreeResult Result: 0x1e52441b950 Allocated memory before freeing: 1
问题原因
Windows下不同C runtime之间的内存分配/释放不兼容:
- 若用MinGW编译Go共享库,其使用MinGW的C runtime(libgcc),
C.CString通过该runtime的malloc分配内存。 - Python默认使用MSVC的C runtime(msvcrt.dll),调用的
C.free是MSVC runtime的函数。
跨runtime释放内存会直接触发崩溃,导致后续代码无法执行。
解决方案
方案1:使用MSVC编译Go共享库
确保Go和Python使用相同的C runtime:
- 打开Visual Studio对应的命令提示符(如x64 Native Tools Command Prompt)。
- 执行编译命令:
go build -buildmode=c-shared -o lib.dll lib.go
编译后的库使用MSVC runtime,和Python的runtime匹配,C.CString分配的内存可正常用C.free释放。
方案2:统一用C标准库分配内存
修改Go的Test函数,直接调用C的malloc分配内存,确保内存分配和释放使用同一runtime:
//export Test func Test(s *C.char) *C.char { goS := C.GoString(s) res := goS + "!" fmt.Println(res) // 用C的malloc分配足够内存(包含结束符) bufSize := C.size_t(len(res) + 1) cPtr := C.malloc(bufSize) if cPtr == nil { return nil } // 将Go字符串复制到C内存 C.memcpy(cPtr, unsafe.Pointer(C.CString(res)), bufSize) // 释放C.CString临时分配的内存 C.free(unsafe.Pointer(C.CString(res))) atomic.AddInt32(&allocCount, 1) return (*C.char)(cPtr) }
这种方式下,内存由C标准库分配,无论用MinGW还是MSVC编译,只要FreeResult中调用的C.free是同一库的函数,就能正常释放。
内容的提问来源于stack exchange,提问作者Stanley Wang
相关产品推荐
相关产品推荐

