Golang Go语言中什么时候返回值什么时候返回指针?

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

Golang Go语言中什么时候返回值什么时候返回指针?
自从了解到“逃逸机制”,写程序时心智负担增加不少。
平时开发时,是否需要有意识地去考虑内存分配问题?
还是更专注于语义?有没有这方面的方法论?

25 回复

这方面的方法论比 go 语言还老得多:
premature optimization is the root of all evil

更多关于Golang Go语言中什么时候返回值什么时候返回指针?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


如果是 C++,就用只能指针啊,不怕泄露,随便用。

只能->智能

gc 语言考虑啥 allocation 问题… 瞎写就是了,先把功能写完,然后再 profiling 优化…
根据我的经验,真正的热点往往不是你想象的那些,所以提前优化确实是万恶之源。

先说什么语言,不同语言虽然语法上看起来都叫指针,但底层原理其实都不太一样,你既然想要在合适的地方使用指针来提高性能,那就要了解所使用语言的底层原理是怎么实现指针的。

逃逸机制能细讲一下?

逃逸机制是指什么?



Google Golang Escape Analysis

逃逸机制是啥 求科普

如果程序不是那种需要注重性能,不要考虑这些。真的需要性能的程序,用什么语言写都一样会增加心智负担。

简单的说,大对象使用指针,小对象,比如只有几个 int 类型字段,可以使用值拷贝,没有明确的分界线可以区分怎么使用,因为还要考虑到实际对象产生的数量,这点只有实际 profiling 才知道。

另外 map 类型的 key/value 也是有讲究的,会影响到 gc 。

不过还是那一句,撸代码的时候不如把时间花在逻辑设计上面,别瞎优化(万一不小心负优化了呢?),跑完再优化。

没必要过早优化吧

本来应该是在栈上分配的,一看外部还保留着引用,这种就会逃逸到堆上,这只是一种简单的场景

逃逸机制没必要在开发阶段考虑吧, 而且大多数情况也影响不到你的业务吧, 后面有影响直接 pprof 优化就是了

写 go 性能早就不在意了,传指针还是值只看用的函数是不是只读这个对象的,是只读的就传值,有修改就传指针

这方便的讨论很多,列举一个我觉得很棒的:
https://stackoverflow.com/questions/23542989/pointers-vs-values-in-parameters-and-return-values

1. Slices, maps, channels, strings, function values, and interface values 内部用指针实现,返回指向它们的指针通常是多余的。
2. 大型结构体或需要更改返回值情况用指针,否则返回值
2.1 大型结构体?当你把结构体的 field 拆分开来放到方法中时你觉得参数很多时,那就是“大型结构体”

反正有 GC,你完全没必要去区分堆栈。

https://golang.org/doc/faq#stack_or_heap

>How do I know whether a variable is allocated on the heap or the stack? 
From a correctness standpoint, you don’t need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.

是否传指针也没必要想太多,基本类型和本身就是引用的传值,其他一律传指针。

感觉 Rust 的 ownership 机制能规避这些问题

前天看了 Go 夜读的内存对齐,也发现之前写的代码或多或少会有点问题(有很多的多余的内存分配),但是我一直觉得这些属于 suggestions 和,真正在这种地方遇到瓶颈的地方的场景很少,至少在平常的业务场景中,把某段业务逻辑设计再精简点,少几次 I/O 操作,优化下算法复杂度,往往产出比花在这些地方要值得。不过,很多常见优化项可以通过配置 IDE/Editor 的 golang ci lint 规则解决就是了

go 故意没有在文档中说堆栈的区别,就是让我们不要去关心堆栈问题。
同意前排的观点,先实现功能,后期再 profile 优化,别想太多。

你看看,我们用易语言的人完全不用担心什么 GC 逃逸机制,指针传递就万事大吉了ε=ε=ε=┏(゜ロ゜;)┛逃

ownership 不是规避这种问题,是强迫你要显式的考虑这种问题。
对于 Golang 来说完全没必要考虑逃逸分析带来的性能影响,都用 Golang 了这种问题交给 GC 和优化器就完了,要不 golang 的优势何在?

语义和内存分配应该不是矛盾的

方法论就是遵循语言的设计。当然 golang 这种允许你选地方的,就参考其他语言,比如小对象直接赋值,大对象用 new 和指针。

大量小对象的场景(比如游戏单位)就搞个对象池,避免重复创建回收。

很多语言没有逃逸分析,还有的语言强制你把对象放堆上(比如 java ),还不是一样写?

不希望值拷贝的时候,就用指针呗

在Golang(Go语言)中,选择返回值还是返回指针通常取决于具体的使用场景和性能考虑。以下是一些指导原则:

  1. 小型数据结构:对于像整型、浮点型、布尔型或小型结构体等小型数据结构,通常直接返回它们的值。这样做的好处是代码简洁,且由于Go语言的值传递特性,性能影响通常可以忽略不计。

  2. 大型数据结构:对于大型结构体或切片等,返回指针更为合适。这样可以避免复制整个数据结构,从而提高性能。此外,如果函数需要修改被传递的数据,也必须返回指针,因为Go语言中的值传递意味着函数内的修改不会影响到原数据。

  3. 接口一致性:如果函数需要返回可能为nil的值,返回指针是一个好选择。这样可以避免返回额外的状态标志来指示错误或空值。

  4. 内存管理:返回指针时,调用者需要负责内存管理,包括在适当的时候释放资源(虽然Go有垃圾回收机制,但在某些情况下仍需注意内存泄漏问题)。

  5. 可读性:代码的可读性也是选择返回值或指针的一个因素。在某些情况下,返回指针可以使代码更清晰,因为它明确指出了数据可能会被修改或生命周期管理的重要性。

总之,在Go语言中,选择返回值还是返回指针是一个权衡过程,需要根据数据结构的大小、性能需求、接口设计和代码可读性等因素综合考虑。

回到顶部