请教一个 golang 的小疑惑 - V2EX
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 3601 次点击
    这是一个创建于 1386 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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
    一样的,你外面传到里面,和里面返回到外面,不都是逃逸吗。你自己用逃逸分析试试看。实践出真知嘛
    shakaraka
        2
    shakaraka  
    PRO
       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     Solana     917 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 18:41 PVG 02:41 LAX 11:41 JFK 14:41
    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