Go Slice 隐式共享陷阱 & 内存对齐
Go Slice 隐式共享陷阱 & 内存对齐
问题代码
1 | package main |
a2 本应是 [1 2 3 4 5 6],结果却是 7。
根本原因:底层数组共享
Go 的 slice 是 { 指针, len, cap } 三元组。
append 在 cap 够用时不分配新数组,直接写入原底层数组。a2 := append(a1, 6) 和 a3 := append(a1, 7) 都基于同一个 a1(len=5),两次都写入 index 5 的同一位置,后者覆盖前者。
1 | a1 → [1][2][3][4][5][ ] len=5, cap=6,index 5 空着 |
为什么 cap 是 6 而不是 5?
内存对齐
CPU 读内存按固定宽度的”块”(通常 8 字节)读取,数据必须存放在对齐地址上:
1 | int64 (8字节) → 必须放在 8 的倍数地址 |
Size Class
Go 的内存分配器(基于 tcmalloc)有固定的 size class,不会分配任意大小的内存:
1 | 需要 5 × 8 = 40 字节 → 最近的 size class 是 48 字节 → 能放 6 个 int64 |
所以 cap 变成 6,多出的一格给了 a2/a3 可直接写入的空位,引发覆盖问题。
解决方案
追加前用完整切片表达式限制 cap,强制 append 分配新数组:
1 | a1 = a1[:len(a1):len(a1)] |
总结
对齐凑整导致 cap 比预期多,这个”意外”的空位是 slice 共享陷阱的导火索。