一、基本数值类型

1.1 整型

整型在内存中存储方式取决于系统架构:

  • 32位系统:4字节
  • 64位系统:8字节

1.2 浮点型

遵循 IEEE 754 标准:

1
2
3
4
5
6
7
8
9
10
11
float32 (32位):
┌────────┬────────────────────────────┐
│ 符号位 │ 指数位(8位) │ 尾数位(23位) │
│ 1bit │ 8bits │ 23bits │
└────────┴────────────────────────────┘

float64 (64位):
┌────────┬─────────────────────────────┬─────────────────────────────┐
│ 符号位 │ 指数位(11位) │ 尾数位(52位) │
│ 1bit │ 11bits │ 52bits │
└────────┴─────────────────────────────┴─────────────────────────────┘

二、字符串 string

2.1 底层结构

1
2
3
4
5
// runtime/string.go
type stringStruct struct {
str unsafe.Pointer // 指向字节数组的指针
len int // 字节长度
}
1
2
3
4
5
6
7
8
9
10
11
12
字符串 "hello" 的内存布局:

stringStruct
┌─────────────┬─────────────┐
│ str (指针) │ len = 5 │
└──────┬──────┴─────────────┘


┌─────┬─────┬─────┬─────┬─────┐
│ 'h' │ 'e' │ 'l' │ 'l' │ 'o' │
└─────┴─────┴─────┴─────┴─────┘
[0] [1] [2] [3] [4]

2.2 关键特性

不可变性:字符串内容存储在只读段,修改会触发新分配。

1
2
s := "hello"
s[0] = 'H' // 编译错误: cannot assign to s[0]

零拷贝切片

1
2
s := "hello world"
sub := s[0:5] // 不复制数据,共享底层字节

三、切片 slice

3.1 底层结构

1
2
3
4
5
6
// runtime/slice.go
type slice struct {
ptr unsafe.Pointer // 指向底层数组
len int // 元素个数
cap int // 容量
}
1
2
3
4
5
6
7
8
9
10
11
切片 s := make([]int, 3, 5):

slice
┌──────────┬──────────┬──────────┐
│ ptr │ len = 3 │ cap = 5 │
└────┬─────┴──────────┴──────────┘


┌───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ │ │
└───┴───┴───┴───┴───┘

3.2 扩容规则

1
2
3
4
5
6
7
8
9
// 扩容规则 (Go 1.18+)
if len < 1024 {
newcap = doublecap // 翻倍
} else {
// 每次增加 1/4
for newcap < newLen {
newcap += newcap / 4
}
}

3.3 切片共享底层数组

1
2
3
4
a := []int{1, 2, 3, 4, 5}
b := a[1:3]

b[0] = 99 // a[1] 也变成 99!

四、映射 map

4.1 底层结构

1
2
3
4
5
6
7
8
9
10
11
12
// runtime/map.go
type hmap struct {
count int // 元素个数
flags uint8 // 状态标志
B uint8 // 桶数量的对数 (2^B个桶)
noverflow uint16 // 溢出桶数量
hash0 uint32 // 哈希种子
buckets unsafe.Pointer // 桶数组指针
oldbuckets unsafe.Pointer // 扩容时的旧桶
nevacuate uintptr // 扩容进度
extra *mapextra // 溢出桶信息
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
map 内部结构:

hmap
┌─────────┬───────┬─────┬──────────┬──────────┐
│ count │ flags │ B │ hash0 │ buckets │
└─────────┴───────┴─────┴──────────┴────┬─────┘


┌─────────────────────┐
│ buckets (2^B 个桶) │
│ ┌────┬────┬────┬────┐│
│ │bmap│bmap│bmap│bmap││
│ └────┴────┴────┴────┘│
└─────────────────────┘

每个桶 bmap:
┌─────────────────────────────┐
│ tophash [8]uint8 │ ← 高8位哈希,快速比对
│ keys [8]keyType │ ← 8个key
│ values [8]valueType │ ← 8个value
│ overflow *bmap │ ← 溢出桶指针
└─────────────────────────────┘

4.2 查找流程

1
2
3
4
5
6
7
查找 map[key]:

1. 计算 hash := hashFunc(key) + hash0
2. 取低B位确定桶: bucket = hash & (2^B - 1)
3. 取高8位快速比对: tophash = hash >> 56
4. 遍历桶内元素,找到则返回
5. 未找到则检查溢出桶

4.3 扩容机制

触发条件

  • 负载因子 > 6.5 (元素太多)
  • 溢出桶过多 (元素分布不均)

扩容是渐进式的,每次操作迁移少量数据。


五、通道 channel

5.1 底层结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// runtime/chan.go
type hchan struct {
qcount uint // 队列中元素个数
dataqsiz uint // 环形队列大小
buf unsafe.Pointer // 环形队列指针
elemsize uint16 // 元素大小
closed uint32 // 是否关闭
elemtype *_type // 元素类型
sendx uint // 发送索引
recvx uint // 接收索引
recvq waitq // 接收等待队列
sendq waitq // 发送等待队列
lock mutex // 互斥锁
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
channel 结构:

hchan
┌─────────────┬─────────────┬─────────────┐
│ qcount │ dataqsiz │ elemsize │
├─────────────┼─────────────┼─────────────┤
│ closed │ sendx │ recvx │
├─────────────┼─────────────┼─────────────┤
│ recvq │ sendq │ lock │
└──────┬──────┴──────┬──────┴─────────────┘
│ │
▼ ▼
recvq 环形队列 buf
(等待队列) ┌───┬───┬───┬───┐
│ 1 │ 2 │ │ │
└───┴───┴───┴───┘

5.2 发送接收流程

1
2
3
4
5
6
7
8
发送 ch <- x:

1. 加锁
2. 检查 recvq 是否有等待者
→ 有:直接把数据拷贝给等待者,唤醒它
3. 检查 buf 是否有空位
→ 有:写入 buf[sendx]
4. 加入 sendq 等待

六、接口 interface

6.1 空接口 eface

1
2
3
4
type eface struct {
_type *_type // 类型信息
data unsafe.Pointer // 数据指针
}

6.2 非空接口 iface

1
2
3
4
5
6
7
8
9
10
11
type iface struct {
tab *itab // 接口表
data unsafe.Pointer // 数据指针
}

type itab struct {
inter *interfacetype // 接口类型
_type *_type // 实际类型
hash uint32 // 类型哈希
fun [1]uintptr // 方法表
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
非空接口 var r io.Reader = &os.File{}:

iface
┌─────────────┬─────────────┐
│ tab (itab) │ data │
└──────┬──────┴──────┬──────┘
│ │
▼ ▼
itab os.File实例
┌─────────────┐
│ inter │ (io.Reader)
│ _type │ (*os.File)
│ fun[0] │ → Read()
└─────────────┘

七、结构体 struct

7.1 内存对齐

1
2
3
4
5
6
type Person struct {
Name string // 16字节
Age int // 8字节
Sex bool // 1字节 + 7字节padding
}
// 总大小: 32字节

7.2 字段重排优化

1
2
3
4
5
6
7
8
9
10
11
12
13
// 未优化: 32字节
type Bad struct {
a bool // 1 + 7 padding
b int64 // 8
c bool // 1 + 7 padding
}

// 优化后: 16字节
type Good struct {
b int64 // 8
a bool // 1
c bool // 1 + 6 padding
}

八、总结

类型 底层核心 关键特性
string ptr + len 不可变、零拷贝切片
slice ptr + len + cap 动态扩容、共享底层数组
map hmap + bmap 哈希桶、渐进扩容
channel hchan + ring buffer 锁 + 等待队列
interface eface/iface + itab 类型信息 + 方法表

理解底层实现有助于写出更高效、更安全的 Go 代码。