Go 中 for 循环中对于某些变量内存需要手动释放的处理方式 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
BenHunDun
V2EX    Go 编程语言
<1>Go 中 for 循环中对于某些变量内存需要手动释放的处理方式
  •  1
     
  •   BenHunDun 119 天前 2136 次点击
    这是一个创建于 119 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

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

    所以问了 Gemini , 给出的方法. 包括 Gemini 提到说可能在编译期会对这个匿名函数做优化. 直接形成高效, 线性的代码 (自己还没有测试, 包括还没有想到如何测试.)
    BenHunDun
        10
    BenHunDun  
    OP
       119 天前
    @dacapoday 自己的环境是 1.24 的版本. 但简单的询问 ChatGPT 说 addCleanup 不适合这个场景. 会自身再确认下 AddCeanup 的作用.
    很感谢, 了解到了新的东西.
    > 如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。
    这部分就是看到用函数的方式缩小了 item 资源的作用域, 感觉上内存会更快的被释放. 然后看到提供了这种方式, 看起来好像不会产生一些新的问题, 但是没碰到该写法, 所以想像 v 站的大佬们问下可行的方式. 或者其他更加优秀的处理方式.
    testcgd
        11
    testcgd  
       118 天前 via Android
    确定范围而且不需要复用,defer 应该是最好的选择了
    testcgd
        12
    testcgd  
       118 天前 via Android
    接 #11 不过抽个函数会更好一点
    everhythm
        13
    everhythm  
       118 天前
    大概懂 lz 的意思,代码开多个实例处理多组数据,但又有内存限制
    普通方案是 new src 实例能复用,串行处理多组数据,也即在数据循环外 new src ,手动管理 close
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     914 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 20:33 PVG 04:33 LAX 12:33 JFK 15:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86