高速公路VS内存

图1. 可以把内存比作汽车高速公路
go-37-001

图2.
go-37-002

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"unsafe"
)

type Salary struct {
s1 int8
s2 int16
}

type Salary2 struct {
s1 int16
s2 int16
}

func main() {
fmt.Println(unsafe.Sizeof(Salary{}))
fmt.Println(unsafe.Sizeof(Salary2{}))
}

输出

4
4

我们可以看到2个struct,size都是4, 2个结构体字段大小不同,但是结构体大小一样。

内存对齐

go-37-003

可以看到第2个int32跨了2个64位的边界,如果你是64位系统,那么CPU每次访问内存,都是拿64位这么多的数据,对于第2个int32跨越2个64位,那么cpu就要分2次拿数据。

go-37-004

好处

  • 提高访问效率
  • 提高内存原子性访问(从CPU角度看,一次操作,就搞定了)

对齐系数

1
2
3
4
5
6
7
fmt.Printf("int64 size:%d align:%d\n", unsafe.Sizeof(int64(1)), unsafe.Alignof(int64(1)))
fmt.Printf("int32 size:%d align:%d\n", unsafe.Sizeof(int32(1)), unsafe.Alignof(int32(1)))
fmt.Printf("int16 size:%d align:%d\n", unsafe.Sizeof(int16(1)), unsafe.Alignof(int16(1)))
fmt.Printf("int8 size:%d align:%d\n", unsafe.Sizeof(int8(1)), unsafe.Alignof(int8(1)))
fmt.Printf("string size:%d align:%d\n", unsafe.Sizeof(string("")), unsafe.Alignof(string("")))
fmt.Printf("bool size:%d align:%d\n", unsafe.Sizeof(bool(true)), unsafe.Alignof(bool(true)))
fmt.Printf("byte size:%d align:%d\n", unsafe.Sizeof(byte(1)), unsafe.Alignof(byte(1)))

int64 size:8 align:8
int32 size:4 align:4
int16 size:2 align:2
int8 size:1 align:1
string size:16 align:8
bool size:1 align:1
byte size:1 align:1

可以看到上面这些基础类型的size和对齐系数是一致的,我们要拿到一个64bit地址,能被这个对齐系数整除的,比如我们要给int16,那么地址可以是 0,2,4,8,这些都可以被2整除。

结构体对齐

**内部对齐规则:**结构体内部每个成员的偏移量自身大小与其对其系数较小值的倍数

1
2
3
4
5
type todo struct {
isCompleted bool
name string
id int32
}

bool 大小1,对齐系数1

string 大小16,对齐系数8

int32 大小4,对齐系数4

go-37-005

可以看到bool占据了一个字节byte,后面都是用0填充,其他的2个字段name和id都是正常占位,在内存中,这3段是连续的。你可能会说bool后面的0都浪费了,为什么不用id去填充,因为结构体顺序是那样写的,不可以把id放到bool的后面,除非改变结构体的内部顺序。

**长度填充规则:**最大成员长度与系统字长较小的整数倍

1
2
3
4
5
type todo struct {
isCompleted bool
name string
id int32
}

bool 大小1,对齐系数1

string 大小16,对齐系数8

int32 大小4,对齐系数4

最长成员是name,string,16字节,本系统是mac的64位,也就是8字节byte,取最小就是8个字节的整数倍。

go-37-006

空结构体

1
2
3
4
5
6
type todo2 struct {
isCompleted bool
x struct{}
name string
id int32
}

go-37-007

看到空结构体在字段中间,没有问题

1
2
3
4
5
6
type todo3 struct {
isCompleted bool
name string
id int32
x struct{}
}

go-37-008

如果最后一个字段是空结构体,那么刚刚赶上是一个字长,比如是64位系统,那么多出来这么一个空结构体,就会顺延占用位置,会导致内存的浪费,这点要注意。

如何学习Go语言微服务,快速步入架构师

从0到Go语言微服务架构师-海报 从0到Go语言微服务架构师
添加微信 公众号更多内容
wechat gzh