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