Go 中 for 循环中对于某些变量内存需要手动释放的处理方式

17 天前
 BenHunDun

自己碰到一个情况, 就是 for 循环中, 每个循环中都创建了变量, 但是因为实际内存不受 GC 管理. 需要手动的进行释放. Gemini 提供了一个方式, 是通过匿名函数 + defer 进行释放

for _, item := range list {
    func(){
	src := new NoGCSource{item}
    	defer src.Close()
    }()
}

这种方式是 go 中处理这类情况, 比较优秀的方式吗. 有什么需要注意和思考的地方吗.

1653 次点击
所在节点    Go 编程语言
13 条回复
iOCZS
17 天前
抛开语言而言,还是要看你要持有多久。defer 本质只是一个作用域内的释放。有些东西你想持有得更久。
maocat
17 天前
sync.pool
BenHunDun
17 天前
@iOCZS #1 就是只有单个循环里处理和使用这个变量, 也不影响到下个循环. 变量也不返回给其他地方.
这个方式是 Gemini 推荐的. 自己细看下来以及向 Gemini 后续提问都没有发现什么问题. 但第一次看到这中处理方式, 还是不确定是不是一种 "好/常用" 的方式
bv
17 天前
defer 是函数级的,每一个 defer 都会向函数栈上记录一个调用信息,在 for 循环里面用 defer 这会占用大量栈空间,可能导致内存压力甚至栈溢出。使用闭包函数算是将 defer 的作用域细分隔离至一个小的函数作用域内。闭包函数执行完,defer 就会立即执行。
cryptovae
17 天前
必然不是一种好方式,这块肯定是要把代码拆分出来作为一个单独的函数,函数自己内部控制变量的 close 状态,你这种 Gemini 告诉你的形式更像是流水账,能用,但是不优雅
bv
17 天前
对此我一般都是将 for 内的函数拆分成单独的函数,例如:
for _, item := range list {
foo(item)
}

func foo(item T) {
src :=NoGCSource(item)
defer src.Close()
// TODO 业务逻辑
}
dacapoday
17 天前
什么 go 版本,官方不是出了 AddCleanup 来管理这种 非 golang runtime 分配的资源?
https://pkg.go.dev/runtime#AddCleanup

而且既然 item 所引用的资源的生命周期仅在一次循环迭代范围内,为什么不在循环体 外声明 item ,在循环体内末尾默认添加 Close 。如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。

或者 item 的生命周期都归 list 管,循环结束后,由 list 统一释放。
lysShub
17 天前
如果不是高频调用的地方,包一个函数,把 c 内存复制到 go 内存中
BenHunDun
17 天前
@cryptovae #5 @bv #6 是在写 Demo 的时候碰到这个问题, 所以一开始没想说特意提一个函数.
原本是打算手动关闭的, 或者想说直接调用 `defer func(src) { src.Close() } ` 去做一个关闭.
> #4
但是手动关闭, 如果碰到一些其他情况可能未关闭, 直接使用 defer 其作用域是 for 外围的函数. 就感觉会中提到的问题.

所以问了 Gemini , 给出的方法. 包括 Gemini 提到说可能在编译期会对这个匿名函数做优化. 直接形成高效, 线性的代码 (自己还没有测试, 包括还没有想到如何测试.)
BenHunDun
17 天前
@dacapoday 自己的环境是 1.24 的版本. 但简单的询问 ChatGPT 说 addCleanup 不适合这个场景. 会自身再确认下 AddCeanup 的作用.
很感谢, 了解到了新的东西.
> 如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。
这部分就是看到用函数的方式缩小了 item 资源的作用域, 感觉上内存会更快的被释放. 然后看到提供了这种方式, 看起来好像不会产生一些新的问题, 但是没碰到该写法, 所以想像 v 站的大佬们问下可行的方式. 或者其他更加优秀的处理方式.
testcgd
16 天前
确定范围而且不需要复用,defer 应该是最好的选择了
testcgd
16 天前
接 #11 不过抽个函数会更好一点
everhythm
16 天前
大概懂 lz 的意思,代码开多个实例处理多组数据,但又有内存限制
普通方案是 new src 实例能复用,串行处理多组数据,也即在数据循环外 new src ,手动管理 close

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1153405

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX