Go 空结构体:零内存的魔力
作者:陈明勇
文章持续更新,如果本文能让您有所收获,欢迎关注本号。
微信阅读可搜《Go 技术干货》。这篇文章已被收录于 GitHub https://github.com/chenmingyong0423/blog,欢迎大家 Star 催更并持续关注。
Go Version → 1.20.4
前言
在 Go
语言中,有一种特殊的用法可能让许多人感到困惑,那就是空结构体 struct{}
。在本文中,我将对 Go
空结构体进行详解,准备好了吗?准备一杯你最喜欢的饮料或茶,随着本文一探究竟吧。
什么是空结构体
不包含任何字段的结构体,就是空结构体。它有以下两种定义方式:
匿名空结构体
命名空结构体
空结构体的特点
空结构体主要有以下几个特点:
零内存占用
地址相同
无状态
零内存占用
空结构体不占用任何内存空间,这使得空结构体在内存优化方面非常有用,我们来通过例子看看是否真的是零内存占用:
通过打印结果对比可知,空结构体内存占用为 0
。
地址相同
无论创建多少个空结构体,它们所指向的地址都相同的。
无状态
由于空结构体不包含任何字段,因此它不能有状态。这使得空结构体在表示无状态的对象或情况时非常有用。
为什么是零内存和地址相同
要理解为什么空结构体在内存上是零大小(零内存)并且多个空结构体的地址是相同的,需要深入研究 Go
的源码。
/go/src/runtime/malloc.go
根据 malloc.go
源码的部分内容,当要分配的对象大小 size
为 0 时,会返回指向 zerobase
的指针。zerobase
是一个用于分配零字节对象的基准地址,它不占用任何实际的内存空间。
空结构体的使用场景
空结构体主要有以下三种使用场景:
实现
Set
集合类型用于通道信号
作为方法接收器
实现 Set 集合类型
在 Go
语言中,虽然没有内置 Set
集合类型,但是我们可以利用 map
类型来实现一个 Set
集合。由于 map
的 key
具有唯一性,我们可以将元素存储为 key
,而 value
没有实际作用,为了节省内存,我们可以使用空结构体作为 value
的值。
用于通道信号
空结构体常用于 Goroutine
之间的信号传递,尤其是不关心通道中传递的具体数据,只需要一个触发信号时。例如,我们可以使用空结构体通道来通知一个 Goroutine
停止工作:
在这个例子中,创建了一个通道 quit
,并在一个单独的 Goroutine
中模拟执行工作。在完成工作后,关闭了 quit
通道,表示退出信号。主函数在 <-quit
处阻塞,直到收到退出信号,然后打印一条消息并退出程序。
由于通道使用的类型是空结构体,因此不会带来额外的内存开销。
在 Go
标准库中,context
包中的 Context
接口的 Done()
方法返回一个通道信号,用于通知相关操作的完成状态。这个通道信号的返回值就是使用了空结构体。
作为方法接收器
有时候我们需要创建一组方法集的实现(一般来说是实现一个接口),但并不需要在这个实现中存储任何数据,这种情况下,我们可以使用空结构体来实现:
这个例子定义了一个接口 Person
和一个结构体 CMY
,并为 CMY
实现了 Person
接口,定义了一组方法(SayHello
和 Sleep
)。
由于 CMY
结构体为空结构体,因此不会带来额外的内存开销。
小结
在本文中,首先介绍了 Go
语言 空结构体 的概念和定义方式,它有两种定义方式;
随后对 空结构体 的特点进行介绍,包括其零内存和多个变量地址相同的特性;
接着进一步深入源码,探究了为什么空结构体在 Go 语言中是零内存且多变量地址相同,原因是当要分配的对象大小 size
为 0 时,会返回指向 zerobase
的指针;
最后列举了空结构体的三个使用场景,通过这些代码示例,展示了空结构体在实际应用中的一些常见用途。
你还知道 空结构体 的其他使用场景吗?欢迎评论区留言探讨。
版权声明: 本文为 InfoQ 作者【陈明勇】的原创文章。
原文链接:【http://xie.infoq.cn/article/87fe1213fcec78d6b4fe4dd95】。文章转载请联系作者。
评论