
自己碰到一个情况, 就是 for 循环中, 每个循环中都创建了变量, 但是因为实际内存不受 GC 管理. 需要手动的进行释放. Gemini 提供了一个方式, 是通过匿名函数 + defer 进行释放
for _, item := range list { func(){ src := new NoGCSource{item} defer src.Close() }() } 这种方式是 go 中处理这类情况, 比较优秀的方式吗. 有什么需要注意和思考的地方吗.
1 iOCZS 119 天前 抛开语言而言,还是要看你要持有多久。defer 本质只是一个作用域内的释放。有些东西你想持有得更久。 |
2 maocat 119 天前 via Android sync.pool |
3 BenHunDun OP @iOCZS #1 就是只有单个循环里处理和使用这个变量, 也不影响到下个循环. 变量也不返回给其他地方. 这个方式是 Gemini 推荐的. 自己细看下来以及向 Gemini 后续提问都没有发现什么问题. 但第一次看到这中处理方式, 还是不确定是不是一种 "好/常用" 的方式 |
4 bv 119 天前 defer 是函数级的,每一个 defer 都会向函数栈上记录一个调用信息,在 for 循环里面用 defer 这会占用大量栈空间,可能导致内存压力甚至栈溢出。使用闭包函数算是将 defer 的作用域细分隔离至一个小的函数作用域内。闭包函数执行完,defer 就会立即执行。 |
5 cryptovae 119 天前 必然不是一种好方式,这块肯定是要把代码拆分出来作为一个单独的函数,函数自己内部控制变量的 close 状态,你这种 Gemini 告诉你的形式更像是流水账,能用,但是不优雅 |
6 bv 119 天前 对此我一般都是将 for 内的函数拆分成单独的函数,例如: for _, item := range list { foo(item) } func foo(item T) { src :=NoGCSource(item) defer src.Close() // TODO 业务逻辑 } |
7 dacapoday 119 天前 什么 go 版本,官方不是出了 AddCleanup 来管理这种 非 golang runtime 分配的资源? https://pkg.go.dev/runtime#AddCleanup 而且既然 item 所引用的资源的生命周期仅在一次循环迭代范围内,为什么不在循环体 外声明 item ,在循环体内末尾默认添加 Close 。如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。 或者 item 的生命周期都归 list 管,循环结束后,由 list 统一释放。 |
8 lysShub 119 天前 如果不是高频调用的地方,包一个函数,把 c 内存复制到 go 内存中 |
9 BenHunDun OP |
10 BenHunDun OP @dacapoday 自己的环境是 1.24 的版本. 但简单的询问 ChatGPT 说 addCleanup 不适合这个场景. 会自身再确认下 AddCeanup 的作用. 很感谢, 了解到了新的东西. > 如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。 这部分就是看到用函数的方式缩小了 item 资源的作用域, 感觉上内存会更快的被释放. 然后看到提供了这种方式, 看起来好像不会产生一些新的问题, 但是没碰到该写法, 所以想像 v 站的大佬们问下可行的方式. 或者其他更加优秀的处理方式. |
11 testcgd 118 天前 via Android 确定范围而且不需要复用,defer 应该是最好的选择了 |
12 testcgd 118 天前 via Android 接 #11 不过抽个函数会更好一点 |
13 everhythm 118 天前 大概懂 lz 的意思,代码开多个实例处理多组数据,但又有内存限制 普通方案是 new src 实例能复用,串行处理多组数据,也即在数据循环外 new src ,手动管理 close |