golang内存对齐的概念及案例详解
来源:脚本之家
时间:2022-12-29 12:41:14 424浏览 收藏
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《golang内存对齐的概念及案例详解》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
什么是内存对齐
为保证程序顺利高效的运行,编译器会把各种类型的数据安排到合适的地址,并占用合适的长度,这就是内存对齐。
每种类型的对齐值就是它的对齐边界,内存对齐要求数据存储地址以及占用的字节数都要是它的对齐边界的倍数。所以下述的int32要错开两个字节,从4开始存,却不能紧接着从2开始。
也可以这样解释:
CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度)。
如果不进行内存对齐
比如我们想从地址1开始读8字节的数据:
CPU会分两次读:
- 第一次从
0 - 7
但只取后7
字节。 - 第二次从
8 - 15
但只取第1
字节。
分两次读,这样势必会对性能造成影响。
为什么要内存对齐
原因主要有两点:
- 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
- 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
对齐边界
那该怎么确定每种数据的对齐边界呢?这和平台有关,go语言支持这些平台:
可以看到常见的32位平台,指针宽度和寄存器宽度都是4字节,64位平台上都是8字节。而被go语言称为寄存器宽度的这个值,就可以理解为机器字长,也是平台对应的最大对齐边界。
而数据类型的对齐边界,是取类型大小与平台最大对齐边界中较小的那个。不过要注意,同一个类型在不同平台上,大小可能不同,对齐边界也可能不同。
为什么不统一使用平台最大对齐边界呢?或者统一按各类型大小来对齐呢?
我们来试一下,假设目前是64位平台,最大对齐边界为8字节。int8只有1字节,按照1字节对齐的话,它可以放在任何位置,因为总能通过一次读取把它完整拿出来。如果统一对齐到8字节,虽然同样只要读取一次,但每个int8的变量都要浪费7字节,所以对齐到1。
int16占2字节,按照2字节对齐,可以从这些地址开始存,而且能保证只用读取一次。
如果按1字节对齐就可能存成这样,那就要读取两次再截取拼接,会影响性能。
如果按8字节对齐,会与int8一样浪费内存,所以对齐到2。
这是小于最大对齐边界的情况,再来看看大于的情况。
假设要在32位的平台下存储一个int64类型的数据,在0和1位置被占用的情况下,就要从位置8开始存。而如果对齐到4,就可以从位置4开始,内存浪费更少,所以选择对齐到4。
因此类型对齐边界会这样选择,依然是为了减少浪费提升性能。
GO 计算对齐边界函数
在go语言中可以调用 unsafe.Alignof
来返回相应类型的对齐边界:
func main() { fmt.Printf("bool align: %d\n", unsafe.Alignof(bool(true))) fmt.Printf("int32 align: %d\n", unsafe.Alignof(int32(0))) fmt.Printf("int8 align: %d\n", unsafe.Alignof(int8(0))) fmt.Printf("int64 align: %d\n", unsafe.Alignof(int64(0))) fmt.Printf("byte align: %d\n", unsafe.Alignof(byte(0))) fmt.Printf("string align: %d\n", unsafe.Alignof("EDDYCJY")) fmt.Printf("map align: %d\n", unsafe.Alignof(map[string]string{})) }
运行结果:
bool align: 1
int32 align: 4
int8 align: 1
int64 align: 8
byte align: 1
string align: 8
map align: 8
确定结构体的对齐边界
对结构体而言,首先要确定每个成员的对齐边界,然后取其中最大的,这就是这个结构体的对齐边界。
然后来存储这个结构体变量:
内存对齐要求一:
- 存储这个结构体的起始地址,是对齐边界的倍数。
假设从0开始存,结构体的每个成员在存储时,都要把这个起始地址当作地址0,然后再用相对地址来决定自己该放在哪里。
内存对齐要求2:
- 结构体整体占用字节数需要是类型对齐边界的倍数,不够的话要往后扩张一下。
所以最终上述结构体类型的大小就是24字节。
案例
type Part1 struct { a bool b int32 c int8 d int64 e byte }
type Part2 struct { a bool c int8 e byte b int32 // 4个字节 d int64 }
分别求以上两个结构体占用的字节:
fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1)) fmt.Printf("part2 size: %d, align: %d\n", unsafe.Sizeof(part2), unsafe.Alignof(part2))
这里我们直接调用函数求得:
part1 size: 32, align: 8 part2 size: 16, align: 8
原因请读者来思考。
参考资料:
https://blog.csdn.net/u010853261/article/details/102557188
https://www.bilibili.com/video/BV1Ja4y1i7AF?from=search&seid=16213689667007976568&spm_id_from=333.337.0.0
今天关于《golang内存对齐的概念及案例详解》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!
-
505 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
365 收藏
-
369 收藏
-
194 收藏
-
213 收藏
-
382 收藏
-
163 收藏
-
370 收藏
-
201 收藏
-
102 收藏
-
467 收藏
-
469 收藏
-
148 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习