如何用CGo避免内存泄漏?Golang调用LightGBM C API内存异常
问题:Golang+CGo调用LightGBM C API导致RSS持续上涨引发OOM
背景:使用Go 1.20.4(Linux环境)开发机器学习模型托管服务,通过CGo封装调用LightGBM的C API,依赖GitHub提供的lib_lightgbm.so库。生产环境部署在内存配额固定的GKE Pod中,业务逻辑为:新模型可用时调用LGBM_BoosterCreateFromModelfile加载,旧模型通过LGBM_BoosterFree释放,循环执行模型刷新操作。
问题现象:模型刷新后服务RSS(常驻内存集)持续上涨。精简代码测试验证:
- 初始RSS为x;
- 调用
Load()后RSS升至y(符合模型加载的内存预期); - 调用
Release()后RSS变为z,z远大于x(约为x的100倍); - 每次Load-Release循环都会导致RSS进一步递增,最终触发GKE Pod OOM。
排查进展:
- 通过读取
/proc/self/statm测量RSS,结果与htop显示数据一致; - Go性能分析显示堆内存指标(heapSys、heapIdle、heapInuse)均处于低位,无法定位是Go内存管理机制还是CGo使用不当导致内存未释放。
测试代码:
package main // #cgo LDFLAGS: -L/home/ayush.goya/minimalExample/ -l_lightgbm // #include "c_api.h" // #include <stdio.h> // #include <stdlib.h> import "C" import ( "runtime/debug" ) var predictor C.BoosterHandle func Load() { outNumIterations := C.int(0) res := int(C.LGBM_BoosterCreateFromModelfile(C.CString("model.txt"), &outNumIterations, &predictor)) debug.FreeOSMemory() println("Load Success") } func Release() { res := int(C.LGBM_BoosterFree(predictor)) debug.FreeOSMemory() println("Release Success") }
RSS测量代码:
func GetRssMB() string { // Read memory statistics from /proc/self/statm data, err := os.ReadFile("/proc/self/statm") if err != nil { fmt.Println("Error reading /proc/self/statm:", err) return "0" } // Extract resident memory size (in pages) fields := strings.Fields(string(data)) if len(fields) < 2 { fmt.Println("Unexpected format of /proc/self/statm") return "0" } rssPages, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { fmt.Println("Error parsing resident memory size:", err) return "0" } // Convert pages to bytes (assuming 4 KB page size) rssBytes := rssPages * 4096 rssBytes /= 1024 * 1024 return strconv.FormatUint(rssBytes, 10) }
内容的提问来源于stack exchange,提问作者Ayush Goyal
相关产品推荐
相关产品推荐

