golang 里面,下面的两个函数,是不是第 2 种更加 GC 友好一些呢?
func aToB (a* A) *B {
}
func aToB (a* A, b *B) {
}
根据我粗浅的认知,第一种写法是不是会出现逃逸,后面还得 GC 去回收呢?
谢谢。
|      1Glauben      2022-01-09 22:20:59 +08:00 via iPhone 一样的,你外面传到里面,和里面返回到外面,不都是逃逸吗。你自己用逃逸分析试试看。实践出真知嘛 | 
|  |      2shakaraka PRO 有啥区别这样 | 
|  |      3wheeler OP | 
|  |      4Trim21      2022-01-09 23:41:02 +08:00 via Android 除非是 unmarshal ,基本没见到第二种写法的 | 
|  |      5lance6716      2022-01-09 23:46:33 +08:00 via Android b 可能在函数里被赋值给了一个全局变量 | 
|  |      6lujjjh      2022-01-10 00:28:53 +08:00  2 某些时候分配在 caller (或者 caller 的 caller……)的 stack 上确实可以避免逃逸到 heap 上,我猜你指的是这个意思。 https://gist.github.com/lujjjh/02d89fd848ee72ce3dd47156fc97c184 理论上编译器应该可以自动优化某些场景(比如上面在函数内联的时候编译器已经能做到避免逃逸到 heap 了),但是在不内联的时候就比较麻烦了,似乎得像真泛型一样为不同场景的 caller 生成不同的函数实现。 | 
|      8neoblackcap      2022-01-10 03:12:05 +08:00 @katsusan b 是可以永远不为 nil 的,因为这是类似 C/C++那套,函数不帮你分配对象,你得自己分配好传进去。至于如何判断错误,加个返回值作为判断操作是否成功就好了。 | 
|      9jinliming2      2022-01-10 09:30:38 +08:00 @wheeler #3 在栈上除非是简单的基本类型,那么在返回的时候也就相当于一个赋值。 如果是对象的话,那肯定是在堆上的,栈上只是一个地址,那么你两种写法应该是一样的。 第一种写法是自己创建一个对象,把地址赋值出去,第二种是外面创建好对象,地址赋值到栈上再传进来(传进来也是一个赋值复制的过程)。 | 
|  |      10fgwmlhdkkkw      2022-01-10 09:33:14 +08:00 都可以。 不过第二个要么返回 bool ,要么返回 error ,更好一点 | 
|  |      11mcfog      2022-01-10 10:07:39 +08:00 via Android 实际上还有第三种写法:既接受入参又返回出参,允许入参 nil 表示内部 allocate ,常见于[]byte | 
|  |      12SSang      2022-01-10 10:19:07 +08:00 你说的对,当指针作为返回值,一定发生逃逸。如果是传值进来,则是可能发生逃逸,所以相对来说更 GC 友好一些,但其实这么对比没什么意义。 而且说实话,这两种写法都不太符合 go 的哲学? 同 Trim21 所说,除非 Unmarshal ,基本不会使用 c++ 的那种写法,在 go 上面开起来就没那么优雅。 如果真要阻止逃逸那大部分会直接写 `func aToB (a A) b B` ,用内存 copy 开销来规避逃逸。 | 
|  |      13SSang      2022-01-10 10:21:33 +08:00  1 `func aToB (a* A, b *B) {}` 这种写法,只是更不容易发生逃逸,但是但凡你函数内用到任意一个会发生逃逸的函数,你前面的工作就全白费了,比如 `fmt.Printf(b)`,所以实际写的时候不会在这两种写法上纠结,因为没法保证一定不逃逸。 | 
|  |      14wheeler OP | 
|  |      15hzzhzzdogee      2022-01-10 13:31:58 +08:00 是的, 第二种不一定发生逃逸.  但是除非有理由, 我个人更推荐第一种写成 func aToB (a* A) B {} | 
|      16hellloworld      2022-01-10 18:00:50 +08:00 b 原本的值还是会逃逸的吧,进入栈内的是地址,差距不大 |