原文地址:Go面试看这里了~(三十)
1、内存对齐?
内存是按照成员的定义顺序来依次分配内存,第一个成员偏移量是0,其余每个成员的偏移量为指定数的整数倍数,如此进行内存分配叫做内存对齐。
2、内存对齐原因?
原因有两点,首先就是平台原因,因为并不是所有硬件平台都能访问任意地址上的任意数据,有的会直接报错的,像有的cpu读取4个字节数据,要是没有内存对齐,从1开始,那内存就需要把0-7字节的全部取出来,再剔除掉1/5/6/7,增加了额外的操作,cpu不一定能这么搞,自然就报错,第二个就是性能原因,访问未对齐的内存,需要访问两次,如果对齐的话就只需要一次,像取int64,按照8个位对齐好之后,获取的话直接就是获取8个字节就好,边界好判断。
3、内存对齐原则?
原则有二,其一是具体类型,对齐值=min(编译器默认对齐值,类型大小Sizeof长度),其二是struct每个字段内部对齐,对齐值=min(默认对齐值,字段最大类型长度)。
4、内存对齐案例分析?
来看个案例:
func main() {
fmt.Println(unsafe.Sizeof(struct {
i8 int8
i16 int16
i32 int32
}{}))
fmt.Println(unsafe.Sizeof(struct {
i8 int8
i32 int32
i16 int16
}{}))
}
输出结果为:
8
12
结构体是平时常用的,由上述输出可看出,相同的成员,不同的排列顺序,需要用到的内存不一致,原因很简单,需要内存对齐的话,因为最大是int32,所以最终结果得是4个字节的倍数才能对齐,当8-16-32的时候,内存存储类似这样|x-xx|xxxx|,当8-32-16的时候,内存存储类似这样|x—|xxxx|xx–|,结果就不言而喻了。
来看使用内存对齐的案例:
type Test struct {
i8 int8
i16 int16
i32 int32
}
func main() {
var t = new(Test)
// 从0开始
var i8 = (*int8)(unsafe.Pointer(t))
*i8 = int8(10)
// 偏移int8+1的字节数,注意这里有个1!!!
var i16 = (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(t))+ uintptr(1) + uintptr(unsafe.Sizeof(int8(0)))))
*i16 = int16(10)
// 偏移int8+1+int16+的字节数,注意这里有个1!!!
var i32 = (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) + uintptr(1) + uintptr(unsafe.Sizeof(int8(0))+uintptr(unsafe.Sizeof(int16(0))))))
*i32 = int32(10)
fmt.Println(*t)
}
上述代码对结构体Test通过指针计算的方式进行赋值,Test内存情况类似这样|x-xx|xxxx|,输出结果为【{10 10 10}】,unsafe.Alignof(t.i16)意为获取对齐值,unsafe.Offsetof(t.i16)意为获取偏移值。
5、内存对齐优化Go程序?
方式有二,其一是根据计算对齐值对struct进行成员顺序的拼凑,可以一定程度上缩小struct占用的内存,其二则为通过分析偏移量和对齐值,准确计算每个成员所偏移的位数,避免算错。
至此,本次分享就结束了,后期会慢慢补充。
以上仅为个人观点,不一定准确,能帮到各位那是最好的。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。