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

20 小时 11 分钟前
 BenHunDun

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

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

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

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

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

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

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

所以问了 Gemini , 给出的方法. 包括 Gemini 提到说可能在编译期会对这个匿名函数做优化. 直接形成高效, 线性的代码 (自己还没有测试, 包括还没有想到如何测试.)
BenHunDun
10 小时 21 分钟前
@dacapoday 自己的环境是 1.24 的版本. 但简单的询问 ChatGPT 说 addCleanup 不适合这个场景. 会自身再确认下 AddCeanup 的作用.
很感谢, 了解到了新的东西.
> 如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。
这部分就是看到用函数的方式缩小了 item 资源的作用域, 感觉上内存会更快的被释放. 然后看到提供了这种方式, 看起来好像不会产生一些新的问题, 但是没碰到该写法, 所以想像 v 站的大佬们问下可行的方式. 或者其他更加优秀的处理方式.

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

https://ex.noerr.eu.org/t/1153405

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

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

© 2021 V2EX