宜宾建设网站,网站备案ip,jsp做网站遇到的问题,网站创建的基本流程关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等#xff0c;您的关注将是我的更新动力#xff01; 在使用Golang进行内存分配时#xff0c;我们需要遵循一系列规则。在深入了解这些规则之前#xff0c;我们需要先了解变量的对齐… 关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等您的关注将是我的更新动力 在使用Golang进行内存分配时我们需要遵循一系列规则。在深入了解这些规则之前我们需要先了解变量的对齐方式。
Golang的unsafe包中有一个函数Alignof签名如下
func Alignof(x ArbitraryType) uintptr对于任何类型为v的变量xAlignOf函数会返回该变量的对齐方式。我们将对齐方式记为m。现在Golang确保m是满足变量x的内存地址 % m 0的最大可能数也就是说变量x的内存地址是m的倍数。
让我们来看看一些数据类型的对齐方式
byte, int8, uint8 - 1int16, uint16 - 2int32, uint32, float32, complex64 - 4int, int64, uint64, float64, complex128 - 8string, slice - 8
对于结构体中的字段行为可能会有所不同详细信息请参考包的文档。
为了更好地理解结构体内存分配的情况我们将使用unsafe包中的另一个函数Offsetof。该函数返回字段相对于结构体起始位置的位置换句话说它返回字段起始位置与结构体起始位置之间的字节数。
func Offsetof(x ArbitraryType) uintptr为了更好地理解结构体内存分配让我们以一个示例结构体为例
type Example struct {a int8b stringc int8d int32
}现在我们将找出类型为Example的变量所占用的总内存并尝试优化分配。
var v Example{a: 10,b: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus rhoncus.,c: 20,d: 100,
}
fmt.Println(字段a的偏移量, unsafe.Offsetof(v.a)) // 输出0
fmt.Println(字段b的偏移量, unsafe.Offsetof(v.b)) // 输出8
fmt.Println(字段c的偏移量, unsafe.Offsetof(v.c)) // 输出24
fmt.Println(字段d的偏移量, unsafe.Offsetof(v.d)) // 输出28现在问题出现了“为什么结构体中字段b的偏移量是8它应该是1因为字段a的类型是int8只占用1个字节。”回到字符串数据类型的对齐方式它的值为8这意味着地址需要被8整除因此在其中插入了7个字节的“填充”以确保这种行为。
为什么字段c的偏移量是24字段b中的字符串看起来比16个字节要长得多如果字符串的偏移量是8那么字段c的偏移量应该更大一些。
上述问题的答案是在Go中字符串并不是在结构体内的同一位置分配内存的。有一个单独的数据结构来保存字符串描述符并且该字符串描述符以原地方式存储在结构体中用于类型为string的字段该描述符的大小为16个字节。
现在让我们来看看unsafe包中的另一个函数Sizeof。正如其名称所示该函数估计并返回类型为x的变量所占用的字节数。
注意它是根据结构体中可能存在的不同大小的字段来估计大小的。
func Sizeof(x ArbitraryType) uintptr现在让我们来看看我们的结构体Example的大小。
fmt.Println(Example的大小, unsafe.Sizeof(v)) // 输出32我们如何优化这个结构体以最小化填充呢
为了优化这个结构体的内存我们将查看不同数据类型的对齐方式并尝试减少填充。让我们尝试将两个int8类型的字段放在一起。
type y struct {a int8c int8b stringd int32
}var v y{}
fmt.Println(字段a的偏移量, unsafe.Offsetof(v.a)) // 输出0
fmt.Println(字段b的偏移量, unsafe.Offsetof(v.b)) // 输出8
fmt.Println(字段c的偏移量, unsafe.Offsetof(v.c)) // 输出1
fmt.Println(字段d的偏移量, unsafe.Offsetof(v.d)) // 输出24
fmt.Println(Example的大小, unsafe.Sizeof(v)) // 输出32太棒了我们去掉了一些填充但是为什么大小仍然是32大小应该是1a 1c 6填充 16b 4d 28
现在当结构体的最后一个字段与架构的对齐要求不完全一致时会在最后一个字段之后添加填充以确保结构体的整体大小是其字段中最大对齐要求的倍数。因为字符串数据类型的最大对齐方式为8所以额外添加了填充使大小成为8的倍数即在末尾填充了4个字节使大小为32字节。
我们能否进一步减少填充使其更加优化
让我们尝试通过移动字段位置来实现。
type y struct {b stringd int32a int8c int8
}var v y{}
fmt.Println(字段a的偏移量, unsafe.Offsetof(v.a)) // 输出20
fmt.Println(字段b的偏移量, unsafe.Offsetof(v.b)) // 输出0
fmt.Println(字段c的偏移量, unsafe.Offsetof(v.c)) // 输出21
fmt.Println(字段d的偏移量, unsafe.Offsetof(v.d)) // 输出16
fmt.Println(Example的大小, unsafe.Sizeof(v)) // 输出24我们可以看到通过重新排列字段的位置使得对齐需要最小化填充我们已经将结构体的大小从32减小到24这是内存优化的巨大进步达到了25%。
当前的内存占用是16b 4d 1a 1b 2填充。
遗憾的是由于语言和架构的限制我们无法进一步去除填充。