Golang Go语言中返回值是否需要尽量返回值,而不返回指针呢?

发布于 1周前 作者 phonegap100 来自 Go语言

理由如下:

根据内存逃逸的规则,把函数局部变量的指针返回会逃逸,结果导致 gc 压力变大。

所以是否需要尽量返回值,而不返回指针呢?

比如查询一个数据模型返回它,还有其他许多情况。


Golang Go语言中返回值是否需要尽量返回值,而不返回指针呢?
31 回复

小类型尽量返回值,大类型看情况

更多关于Golang Go语言中返回值是否需要尽量返回值,而不返回指针呢?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我是 结构体 指针否 值

对看情况 有些情况下返回值可能压力更大

我写 go 基本都有指针,除非是明确返回不可修改的值才用值返回。写 web 应用 gc 压力不存在的,pprof 看下,全耗在序列化上了

不要过早地进行优化
绝大部分项目的性能要求还没高到需要抠这种细节的地步

八股就是这么衍生出来的吗。。。大概按 1L 说的就能避免 80%的你认为的不合理,然后真的哪天出现了性能问题再去 pprof ,而且大概率不会是这个原因

结构体统统用指针,普通类型用值。因为结构体你不知道啥时候加需求往里面加大类型,而且结构体一般会到处传,如果所有地方都用值传递,每次赋值都内存拷贝一份也是有开销的,指针的话只拷贝指针地址更快。

另外 https://www.cnblogs.com/janbar/p/17072751.html 这篇文章探讨了直接赋值的深拷贝问题,即使你值传递,结构体内部有指针,那么这些指针在赋值时也是赋值指针地址。如果用值值类型,你到时候还得思考结构体内部哪些字段是深拷贝,哪些是浅拷贝。

http 提供下面方式克隆对象,就是因为值传递内部字段也存在浅拷贝问题,需要编写深拷贝代码。与其值传递增加心智负担,还不如无脑指针传递。
func (r *Request) Clone(ctx context.Context) *Request {

综上所述,我觉得结构体一律用指针。

我在用的 [go-zero]( https://github.com/zeromicro/go-zero/pull/1211/files#diff-a650192c5b74f391823e44c0b326c07abe5c2544ab386b1ce73ce6b293d78a4c) 框架,在这次改动中将参数值传递改为指针传递,导致我某次升级改了好多文件代码,连大佬都觉得结构体指针传递好些吧。


如果你明确的知道你需要值传递,并且清楚这个对象赋值后内部字段存在浅拷贝也不会影响逻辑,那么可以用值传递。

https://go.dev/wiki/CodeReviewComments#pass-values

有时候也不要只看内存尺寸,而忽视了栈性能远快于堆性能,以及用指针带来了内存逃逸开销,是需要综合考虑的。

在写代码的时候,尽量把方法往 值传递优化 和 不可变数据 去设计和实现,对象之海迟早会反噬的…

我也认为应该尽量用指针,如果为了减少 GC ,可以用 sync.Pool 重用它。

我的理解是以业务逻辑为核心,99%的业务返回值,而 1%的业务是很明显需要指针的,对于不能完全理解以上描述的开发者先返回值是没问题的。

你得了解 go 的引用类型,chan 是引用类型,直接返回就是引用。作为返回值,引用类型本身类似指针,返回的就是引用。作为参数在引用类型加指针貌似只有需要修改引用类型的时候才用到吧。

是的, 对于降低 p99 有明显的效果

小型数据结构:如果是小型结构或不频繁修改的数据,优先返回值以减少 GC 压力。
大型或复杂对象:对于大型或需要频繁修改的对象,考虑返回指针以避免大量数据复制。

之前看过这篇,性能上大部分情况下是返回 struct 更快:
https://cloud.tencent.com/developer/article/1861199

不过如果要和 nil 区分,或者最终要放到堆里,那就继续用指针。

然后如果是参数的话,记得是超过 32 字节传指针更快,而且每个字段是单独用一个 MOV 指令来复制的,不是一整块复制的。

虽然返回指针会导致逃逸,但是存在即合理



纯讨论一个点,不加入其他无压力,性能没影响什么的类似的额外影响话题。

字符串直接传不会因为传值复制导致内存激增吗,我的理解会复制一个相同大小的字符串

比如有些 orm 库的数据库查询,都不是直接返回结果 model 的指针,而是传指针进去,数据填充到里面。

有些又是直接返回结果 model 的指针。

字符串是浅拷贝 StringHeader

当然不会,golang 的字符串本质上就不是一个纯值类型。

orm 一般用到反射,反射会引起逃逸,所以直接用指针了

orm 用参数当返回值的另一个点,就是需要调用方来控制这段内存的申请和销毁(在 go 里,你可以用 sync.Pool 来优化)。 也是 C/C++里面的原则吧,谁声明,谁处理, 调用方 malloc 了 ,调用方来 free ,方法内部是不会帮你初始化堆上的内存的。

都在 orm 上了各种反射

尽量用值吧,编译器会帮你优化的

尽量用值,除非你的业务量真的到需要用这种细节来优化了,不过真的到时候也是应该先 bench 一下。
我们的业务代码基本不用指针,入参,出参都不用,推荐方法无状态,副作用可控,容易测试。

字符串(数组)的内部实现: https://go.dev/blog/slices

擦,一直以为 go 的 string 是复制一个字符串,我知道内部实现是这玩意, 但是也没深入看过, 学习了学习了。。

在Golang(Go语言)中,关于返回值是应该尽量返回值本身,还是返回指针,这取决于具体的使用场景和性能需求。

一般来说,对于小型数据结构(如基本数据类型、小型结构体等),直接返回值是更简洁和直观的做法。这样做可以减少内存分配和指针操作的开销,同时代码也更容易理解和维护。

然而,对于大型数据结构或需要在多个函数间共享和修改的数据,返回指针可能更为合适。这可以避免数据拷贝带来的性能开销,并且允许函数直接修改原始数据。此外,在某些情况下,返回指针还可以实现更灵活的接口设计,比如返回接口类型的指针,以实现多态性。

需要注意的是,返回指针时需要谨慎处理内存管理问题,以避免内存泄漏或野指针等错误。特别是在使用goroutine和并发编程时,要确保对共享数据的访问是安全的。

总的来说,在Go语言中,返回值还是返回指针并没有绝对的优劣之分,而是需要根据具体情况进行权衡和选择。在设计函数接口时,应该考虑数据的性质、性能需求以及代码的可读性和可维护性,从而做出最合适的决策。

因此,无法一概而论地说在Go语言中返回值一定要优于返回指针,或者反之。正确的做法是根据实际情况灵活选择。

回到顶部