一文探索Go语言中的内存对齐

 更新时间:2024年11月22日 15:58:41   作者:左诗右码  
在 Go 语言中,内存对齐是一个经常被忽略但非常重要的概念,本文将通过一个简单的例子来探讨 Go 语言中的内存对齐机制,感兴趣的可以了解下

在 Go 语言中,内存对齐是一个经常被忽略但非常重要的概念。理解内存对齐不仅可以帮助我们写出更高效的代码,还能避免一些潜在的性能陷阱。

在这篇文章中,我们将通过一个简单的例子来探讨 Go 语言中的内存对齐机制,以及为什么相似的结构体在内存中会占用不同的大小。

示例代码

我们先来看一段代码:

package memory_alignment

import (
	"fmt"
	"unsafe"
)

type A struct {
	a int8
	b int8
	c int32
	d string
	e string
}

type B struct {
	a int8
	e string
	c int32
	b int8
	d string
}

func Run() {
	var a A
	var b B
	fmt.Printf("a size: %v \n", unsafe.Sizeof(a))
	fmt.Printf("b size: %v \n", unsafe.Sizeof(b))
	// a size: 40
	// b size: 48
}

在这个例子中,我们定义了两个结构体 AB。它们的字段基本相同,只是排列顺序不同。然后,我们使用 unsafe.Sizeof 来查看这两个结构体在内存中的大小。

结果却令人惊讶:结构体 A 的大小是 40 字节,而结构体 B 的大小是 48 字节。为什么会出现这样的差异呢?这就是我们今天要讨论的内存对齐的作用。

内存对齐概念

内存对齐是指编译器为了优化内存访问速度,而对数据在内存中的位置进行调整的一种策略。不同类型的数据在内存中的对齐要求不同,例如:

  • int8 类型的变量通常对齐到 1 字节边界。
  • int32 类型的变量通常对齐到 4 字节边界。
  • 指针(如 string)通常对齐到 8 字节边界。

为了满足这些对齐要求,编译器可能会在结构体的字段之间插入一些“填充”字节,从而确保每个字段都能正确对齐。

结构体内存布局解析

让我们深入分析一下 AB 两个结构体的内存布局,看看编译器是如何为它们分配内存的。

结构体 A 的内存布局

| a (int8) | b (int8) | padding (2 bytes) | c (int32) | d (string, 8 bytes) | e (string, 8 bytes) |
  • abint8 类型,各占 1 字节。
  • cint32 类型,需要 4 字节对齐,b 后面会有 2 个填充字节。
  • destring 类型,各占 8 字节。

总大小为:1 + 1 + 2 + 4 + 8 + 8 = 24 字节。

结构体 B 的内存布局

| a (int8) | padding (7 bytes) | e (string, 8 bytes) | c (int32) | padding (4 bytes) | b (int8) | padding (3 bytes) | d (string, 8 bytes) |
  • aint8 类型,占 1 字节,后面有 7 个填充字节,以便 e 能够对齐到 8 字节边界。
  • cint32 类型,需要 4 字节对齐,因此在 c 后面没有填充。
  • bint8 类型,需要填充 3 个字节来对齐到 d 的 8 字节边界。

总大小为:1 + 7 + 8 + 4 + 4 + 1 + 3 + 8 = 36 字节。

请注意,Go 编译器可能会将 de 视为 8 字节对齐类型(取决于系统和编译器的实现),因此总大小可能是 48 字节。

如何优化结构体内存布局

为了减少结构体的内存占用,我们可以按照字段的对齐要求来重新排列字段。例如:

先声明大的字段(如 stringint32),然后是小的字段(如 int8),可以减少内存中的填充字节。

我们可以将 B 结构体改成以下形式:

type OptimizedB struct {
    e string
    d string
    c int32
    a int8
    b int8
}

这样可以减少内存填充,从而优化内存占用。

总结

内存对齐是编译器优化内存访问速度的一个重要策略。虽然它对大多数应用程序的影响可能较小,但在高性能场景或内存受限的环境中,理解并优化内存对齐可能会带来显著的性能提升。

在 Go 语言中,了解结构体的内存对齐规则,合理排列结构体字段顺序,不仅可以提高程序的性能,还能减少内存的浪费。这是一种简单而有效的优化手段,希望大家在以后的编程实践中能够灵活运用。

到此这篇关于一文探索Go语言中的内存对齐的文章就介绍到这了,更多相关Go内存对齐内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一键定位Golang线上服务内存泄露的秘籍

    一键定位Golang线上服务内存泄露的秘籍

    内存泄露?别让它拖垮你的Golang线上服务!快速掌握Go语言服务内存泄漏排查秘籍,从此问题无处遁形,一文读懂如何精准定位与有效解决Golang应用中的内存问题,立即阅读,让性能飞升!
    2024-01-01
  • Mac GoLand打不开(闪退)也不报错的解决方案

    Mac GoLand打不开(闪退)也不报错的解决方案

    这篇文章主要介绍了Mac GoLand打不开(闪退)也不报错的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Go单元测试对数据库CRUD进行Mock测试

    Go单元测试对数据库CRUD进行Mock测试

    这篇文章主要为大家介绍了Go单元测试对数据库CRUD进行Mock测试的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go用两个协程交替打印100以内的奇偶数的方法详解

    Go用两个协程交替打印100以内的奇偶数的方法详解

    这篇文章主要给大家详细介绍了Go用两个协程交替打印100以内的奇偶数的示例代码,文中给大家介绍了两个实现方法,使用无缓冲的channel和设置GOMAXPROCS=1,介绍的非常详细,需要的朋友可以参考下
    2023-08-08
  • Golang实现Directional Channel(定向通道)

    Golang实现Directional Channel(定向通道)

    这篇文章主要介绍了Golang实现Directional Channel(定向通道),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Go语言中Slice常见陷阱与避免方法详解

    Go语言中Slice常见陷阱与避免方法详解

    这篇文章主要为大家详细介绍的是 Go 语言中的 Slice 的常见陷阱以及如何避免这些错误,文中的示例代码讲解详细,感兴趣的小伙伴可以学习一下
    2023-02-02
  • go语言中if语句用法实例

    go语言中if语句用法实例

    这篇文章主要介绍了go语言中if语句用法,以实例形式分析了if语句的定义及使用技巧,非常具有实用价值,需要的朋友可以参考下
    2015-02-02
  • go-zero 组件布隆过滤器使用示例详解

    go-zero 组件布隆过滤器使用示例详解

    这篇文章主要为大家介绍了go-zero组件介绍之布隆过滤器使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • golang生成vcf通讯录格式文件详情

    golang生成vcf通讯录格式文件详情

    这篇文章主要介绍了golang生成vcf通讯录格式文件详情,​VCF是通讯录格式文件,一般需要用手机通讯录导入导出的文件格式都是vcf格式。​下面详细内容介绍需要的小伙伴可以参考一下
    2022-03-03
  • go项目中环境变量的配置

    go项目中环境变量的配置

    本文主要介绍了go项目中环境变量的配置,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07

最新评论