Golang 里面选择 []T 还是 []*T?
我看到公司项目里面无论是方法参数还是返回值全部都是*T,也就是都是指针
根据很多文章介绍,Go 里面只存在值传递,但是如果作为函数返回值来用,加个指针意义何在?
func something()([]*T) { }
经过我测试,加上指针后内存分配的次数反而多了,性能有所下降
我看到公司项目里面无论是方法参数还是返回值全部都是*T,也就是都是指针
根据很多文章介绍,Go 里面只存在值传递,但是如果作为函数返回值来用,加个指针意义何在?
func something()([]*T) { }
经过我测试,加上指针后内存分配的次数反而多了,性能有所下降
https://github.com/google/go-github/issues/180
我们通过一些代码示例来演示了在 Go 中值类型和指针类型的一些具体表现,最后我们要回答这么几个问题,希望你能够在使用 Go 编程的过程中更加清晰的掌握这些技巧。Receiver Type 为什么推荐使用指针?
* 推荐在实例方法上使用指针(前提是这个类型不是一个自定义的 map 、slice 等引用类型)
* 当结构体较大的时候使用指针会更高效
* 如果要修改结构内部的数据或状态必须使用指针
* 当结构类型包含 sync.Mutex 或者同步这种字段时,必须使用指针以避免成员拷贝
* 如果你不知道该不该使用指针,使用指针!
“结构较大” 到底多大才算大可能需要自己或团队衡量,如超过 5 个字段或者根据结构体内占用来计算。方法参数该使用什么类型?
* map 、slice 等类型不需要使用指针(自带 buf )
* 指针可以避免内存拷贝,结构大的时候不要使用值类型
* 值类型和指针类型在方法内部都会产生一份拷贝,指向不同
* 小数据类型如 bool 、int 等没必要使用指针传递
* 初始化一个新类型时(像 NewEngine() *Engine )使用指针
* 变量的生命周期越长则使用指针,否则使用值类型
– 不用指针是值传递,基本就不会走 gc,缺点是导致整个 struct 发生内存拷贝,当然被编译器识别为 inline 函数就什么都不会发生,快得一匹。当你函数输入 /返回参数,struct 就几个 int,float,值传递吧!
– 指针就是类似引用传递,出作用域会走 gc,当然也不是绝对,比如 inline 函数返回指针就不一定会导致堆分配,当然内置的 new 和 make,map,slice 等本身就分配在堆上就必然走 gc 。
– gc 对于密集型计算服务的后果就是大量 cpu 计算都消耗在 gc 上,严重影响性能,另外栈内存的分配花销时间可能比堆好
– 其实在 Go 中,除了方法属主参数,指针使用的并不普遍,使用指针参数的主要目的是在函数内能够更改此指针所引用的值。指针有时候也用来避免较大的值赋值代价。比如将一个指针包裹在接口值中的代价比将一个非接口值包裹到接口值中的代价小得多。另一方面,过多的指针会影响垃圾回收的时长,一般这种情况发生在一个容器中含有大量包含指针的元素。
我是新手(上海及南方求 Go 坑位)
哈哈,各位老哥是认真的吗?
之所以返回指针,也是跟值复制有关的。如果是返回 []T,则函数内会有一个 []T,然后再复制一个 []T 作为返回赋值给调用函数的地方。此时,就有 2 份 []T 了。
如果返回 []*T 呢?同样也是有 2 份 []*T 的,但此时的 2 份,只是 2 份“引用”而已,嗯…2 份“内存地址”和一份真正存了值的内存。怎么看内存的占用都比 2 份完整的 []T 所占用的内存要小吧
这是我的回答,如有错误,欢迎指教
使用 Go 自带 bench 测试,带上了 benchmem,得出的 ns/op 、allocs/op 结果带指针的都大一点,但是 B/op 小一点
这里的区别是往 []T 填充元素的时候,如果 T 结构体很大,那么使用指针能减少拷贝值的体积。
个人觉得没有性能上的差别,更应该站在语义上考虑,一般情况下用[]*T,如果你不想元素被修改(语义上)用[]T