V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
BenHunDun
V2EX  ›  Go 编程语言

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

  •  1
     
  •   BenHunDun · 13 小时 7 分钟前 · 835 次点击

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

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

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

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

    func foo(item T) {
    src :=NoGCSource(item)
    defer src.Close()
    // TODO 业务逻辑
    }
    dacapoday
        7
    dacapoday  
       12 小时 10 分钟前   ❤️ 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  
       7 小时 19 分钟前
    如果不是高频调用的地方,包一个函数,把 c 内存复制到 go 内存中
    BenHunDun
        9
    BenHunDun  
    OP
       3 小时 30 分钟前
    @cryptovae #5 @bv #6 是在写 Demo 的时候碰到这个问题, 所以一开始没想说特意提一个函数.
    原本是打算手动关闭的, 或者想说直接调用 `defer func(src) { src.Close() } ` 去做一个关闭.
    > #4
    但是手动关闭, 如果碰到一些其他情况可能未关闭, 直接使用 defer 其作用域是 for 外围的函数. 就感觉会中提到的问题.

    所以问了 Gemini , 给出的方法. 包括 Gemini 提到说可能在编译期会对这个匿名函数做优化. 直接形成高效, 线性的代码 (自己还没有测试, 包括还没有想到如何测试.)
    BenHunDun
        10
    BenHunDun  
    OP
       3 小时 17 分钟前
    @dacapoday 自己的环境是 1.24 的版本. 但简单的询问 ChatGPT 说 addCleanup 不适合这个场景. 会自身再确认下 AddCeanup 的作用.
    很感谢, 了解到了新的东西.
    > 如果怕循环体内 panic ,也可以在 item 声明时,添加 defer ,检测 panic 发生,并释放此时 item 引用的资源。
    这部分就是看到用函数的方式缩小了 item 资源的作用域, 感觉上内存会更快的被释放. 然后看到提供了这种方式, 看起来好像不会产生一些新的问题, 但是没碰到该写法, 所以想像 v 站的大佬们问下可行的方式. 或者其他更加优秀的处理方式.
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1368 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 17:37 · PVG 01:37 · LAX 10:37 · JFK 13:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.