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

请教一个 golang 的小疑惑

  •  
  •   wheeler · 2022-01-09 21:40:40 +08:00 · 3291 次点击
    这是一个创建于 1048 天前的主题,其中的信息可能已经有所发展或是发生改变。

    golang 里面,下面的两个函数,是不是第 2 种更加 GC 友好一些呢?

    
    func aToB (a* A) *B {
    
    }
    
    func aToB (a* A, b *B) {
    
    }
    
    

    根据我粗浅的认知,第一种写法是不是会出现逃逸,后面还得 GC 去回收呢?

    谢谢。

    16 条回复    2022-01-10 18:00:50 +08:00
    Glauben
        1
    Glauben  
       2022-01-09 22:20:59 +08:00 via iPhone
    一样的,你外面传到里面,和里面返回到外面,不都是逃逸吗。你自己用逃逸分析试试看。实践出真知嘛
    wunonglin
        2
    wunonglin  
       2022-01-09 22:25:41 +08:00
    有啥区别这样
    wheeler
        3
    wheeler  
    OP
       2022-01-09 22:42:00 +08:00
    @2435043xia
    @wunonglin
    第二种情况 b 完全可以走栈内存啊。
    Trim21
        4
    Trim21  
       2022-01-09 23:41:02 +08:00 via Android
    除非是 unmarshal ,基本没见到第二种写法的
    lance6716
        5
    lance6716  
       2022-01-09 23:46:33 +08:00 via Android
    b 可能在函数里被赋值给了一个全局变量
    lujjjh
        6
    lujjjh  
       2022-01-10 00:28:53 +08:00   ❤️ 2
    某些时候分配在 caller (或者 caller 的 caller……)的 stack 上确实可以避免逃逸到 heap 上,我猜你指的是这个意思。

    https://gist.github.com/lujjjh/02d89fd848ee72ce3dd47156fc97c184

    理论上编译器应该可以自动优化某些场景(比如上面在函数内联的时候编译器已经能做到避免逃逸到 heap 了),但是在不内联的时候就比较麻烦了,似乎得像真泛型一样为不同场景的 caller 生成不同的函数实现。
    katsusan
        7
    katsusan  
       2022-01-10 01:23:13 +08:00
    @wheeler 第二种不好 hold 住返回的 b 为 nil 的 case
    neoblackcap
        8
    neoblackcap  
       2022-01-10 03:12:05 +08:00
    @katsusan b 是可以永远不为 nil 的,因为这是类似 C/C++那套,函数不帮你分配对象,你得自己分配好传进去。至于如何判断错误,加个返回值作为判断操作是否成功就好了。
    jinliming2
        9
    jinliming2  
       2022-01-10 09:30:38 +08:00
    @wheeler #3
    在栈上除非是简单的基本类型,那么在返回的时候也就相当于一个赋值。
    如果是对象的话,那肯定是在堆上的,栈上只是一个地址,那么你两种写法应该是一样的。
    第一种写法是自己创建一个对象,把地址赋值出去,第二种是外面创建好对象,地址赋值到栈上再传进来(传进来也是一个赋值复制的过程)。
    fgwmlhdkkkw
        10
    fgwmlhdkkkw  
       2022-01-10 09:33:14 +08:00
    都可以。
    不过第二个要么返回 bool ,要么返回 error ,更好一点
    mcfog
        11
    mcfog  
       2022-01-10 10:07:39 +08:00 via Android
    实际上还有第三种写法:既接受入参又返回出参,允许入参 nil 表示内部 allocate ,常见于[]byte
    SSang
        12
    SSang  
       2022-01-10 10:19:07 +08:00
    你说的对,当指针作为返回值,一定发生逃逸。如果是传值进来,则是可能发生逃逸,所以相对来说更 GC 友好一些,但其实这么对比没什么意义。

    而且说实话,这两种写法都不太符合 go 的哲学?
    同 Trim21 所说,除非 Unmarshal ,基本不会使用 c++ 的那种写法,在 go 上面开起来就没那么优雅。
    如果真要阻止逃逸那大部分会直接写 `func aToB (a A) b B` ,用内存 copy 开销来规避逃逸。
    SSang
        13
    SSang  
       2022-01-10 10:21:33 +08:00   ❤️ 1
    `func aToB (a* A, b *B) {}`

    这种写法,只是更不容易发生逃逸,但是但凡你函数内用到任意一个会发生逃逸的函数,你前面的工作就全白费了,比如 `fmt.Printf(b)`,所以实际写的时候不会在这两种写法上纠结,因为没法保证一定不逃逸。
    wheeler
        14
    wheeler  
    OP
       2022-01-10 12:09:13 +08:00 via iPhone
    看到了官方的 FAQ:

    https://go.dev/doc/faq#stack_or_heap
    hzzhzzdogee
        15
    hzzhzzdogee  
       2022-01-10 13:31:58 +08:00
    是的, 第二种不一定发生逃逸.

    但是除非有理由, 我个人更推荐第一种写成 func aToB (a* A) B {}
    hellloworld
        16
    hellloworld  
       2022-01-10 18:00:50 +08:00
    b 原本的值还是会逃逸的吧,进入栈内的是地址,差距不大
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2614 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:00 · PVG 19:00 · LAX 03:00 · JFK 06:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.