Golang Slice 深度解析
基于 Go 1.26.4 源码,源码路径:
github.com/go-go1.26.4核心源文件:
runtime/slice.go、reflect/value.go
1 slice 功能完整介绍
1.1 语法定义与核心概念
Go 切片(slice)是对底层数组的引用视图,由三个字段组成:指针、长度、容量。
go
// 声明(零值 nil)
var s []int // s == nil, len=0, cap=0
// make 初始化
s = make([]int, 5) // len=5, cap=5, 元素全为0
s = make([]int, 3, 10) // len=3, cap=10
// 字面量初始化
s := []int{1, 2, 3} // len=3, cap=3
s := []int{10: -1} // len=11, cap=11, s[10]=-1 其余为0
// 从数组切片
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:3] // len=2, cap=4, s=[2,3]
// new(只分配 slice header,不分配底层数组)
sp := new([]int) // *[]int, *sp == nil
1.2 slice vs array 核心区别
| 维度 | Array [n]T |
Slice []T |
|---|---|---|
| 长度 | 编译期固定,属于类型 | 运行时可变 |
| 内存 | 值类型,直接存储数据 | 引用类型,3个字段(24字节) |
| 赋值 | 拷贝全部数据 | 拷贝 header(共享底层数组) |
| 传参 | 拷贝整份数组 | 拷贝 header(高效) |
| nil | 不存在 nil | nil slice 合法 |
| 比较 | 可用 == 比较 | 不可用 == 比较(只能与 nil 比) |
| 零值 | 元素全为零值 | nil |
1.3 适用场景
#mermaid-svg-mxRpxGPTrqcq7lpl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mxRpxGPTrqcq7lpl .error-icon{fill:#552222;}#mermaid-svg-mxRpxGPTrqcq7lpl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mxRpxGPTrqcq7lpl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mxRpxGPTrqcq7lpl .marker.cross{stroke:#333333;}#mermaid-svg-mxRpxGPTrqcq7lpl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mxRpxGPTrqcq7lpl p{margin:0;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge{stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 text{fill:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth--1{stroke-width:17;}#mermaid-svg-mxRpxGPTrqcq7lpl .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-0{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-0{stroke-width:14;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-1{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-1{stroke-width:11;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 text{fill:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-2{stroke-width:8;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-3{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-3{stroke-width:5;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-4{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-4{stroke-width:2;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-5{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-5{stroke-width:-1;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-6{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-6{stroke-width:-4;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-7{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-7{stroke-width:-7;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-8{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-8{stroke-width:-10;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-9{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-9{stroke-width:-13;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 polygon,#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 text{fill:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .node-icon-10{font-size:40px;color:black;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .edge-depth-10{stroke-width:-16;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled circle,#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:lightgray;}#mermaid-svg-mxRpxGPTrqcq7lpl .disabled text{fill:#efefef;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-root rect,#mermaid-svg-mxRpxGPTrqcq7lpl .section-root path,#mermaid-svg-mxRpxGPTrqcq7lpl .section-root circle,#mermaid-svg-mxRpxGPTrqcq7lpl .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-mxRpxGPTrqcq7lpl .section-root text{fill:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-root span{color:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .section-2 span{color:#ffffff;}#mermaid-svg-mxRpxGPTrqcq7lpl .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-mxRpxGPTrqcq7lpl .edge{fill:none;}#mermaid-svg-mxRpxGPTrqcq7lpl .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-mxRpxGPTrqcq7lpl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go Slice 适用场景
动态集合
不确定元素数量
频繁增删
函数参数
避免数组拷贝
传切片而非数组
字符串处理
byte切片操作
rune切片转换
IO缓冲区
读取写入缓冲
零拷贝切片引用
数据管道
生产者append
消费者slice消费
2 底层 runtime 实现原理
2.1 slice 底层结构体完整字段
源码位置:runtime/slice.go:14
go
type slice struct {
array unsafe.Pointer // ★ 指向底层数组的指针
len int // ★ 当前元素数量
cap int // ★ 底层数组容量(从 array 起始到末尾的元素数)
}
内存布局(64-bit 系统,24 bytes):
slice header (24 bytes on 64-bit)
┌────────────────────────────────────────────────────┐
│ array unsafe.Pointer (8B) ──→ 底层数组首地址 │
├────────────────────────────────────────────────────┤
│ len int (8B) │ 当前长度 │
├────────────────────────────────────────────────────┤
│ cap int (8B) │ 容量 │
└────────────────────────────────────────────────────┘
底层数组:
array ──→ [elem0][elem1][elem2]...[elem_len-1][ ... 已分配但未使用 ... ]
│←──── len ────→│ │←──── cap - len ────→│
│←──────────────── cap ──────────────────────→│
三种 slice 状态的内存布局:
1. nil slice:
┌───────────┐
│ array=nil │
│ len = 0 │
│ cap = 0 │
└───────────┘
底层数组: 无
s == nil → true
2. 空 slice (make([]int, 0) 或 s[:0]):
┌──────────────────────┐
│ array = &zerobase │ ← 指向全局零值地址
│ len = 0 │
│ cap = 0 │
└──────────────────────┘
底层数组: zerobase(全局唯一的零字节)
s == nil → false ★ 注意区别!
3. 正常 slice:
┌──────────────────────┐ ┌────┬────┬────┬────┬────┬────┐
│ array ────────────── │────→│ 10 │ 20 │ 30 │ 0 │ 0 │ 0 │
│ len = 3 │ └────┴────┴────┴────┴────┴────┘
│ cap = 6 │ [0] [1] [2] [3] [4] [5]
└──────────────────────┘ │←─ len ─→│←── 可增长 ──→│
│←──────── cap ──────────→│
2.2 makeslice------创建切片的内存分配
源码位置:runtime/slice.go:57
go
func makeslice(et *_type, len, cap int) unsafe.Pointer {
// ★ 计算所需内存 = cap × elemSize
mem, overflow := math.MulUintptr(et.Size_, uintptr(cap))
// ★ 溢出或超过最大分配或参数非法 → panic
if overflow || mem > maxAlloc || len < 0 || len > cap {
mem, overflow := math.MulUintptr(et.Size_, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen() // panic: makeslice: len out of range
}
panicmakeslicecap() // panic: makeslice: cap out of range
}
// ★ 分配内存
return mallocgc(mem, et, true)
// mallocgc 第三个参数 true = 需要零初始化
// 如果 et 包含指针,GC 会扫描这块内存
}
#mermaid-svg-thijnvge6LkijG7x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-thijnvge6LkijG7x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-thijnvge6LkijG7x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-thijnvge6LkijG7x .error-icon{fill:#552222;}#mermaid-svg-thijnvge6LkijG7x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-thijnvge6LkijG7x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-thijnvge6LkijG7x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-thijnvge6LkijG7x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-thijnvge6LkijG7x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-thijnvge6LkijG7x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-thijnvge6LkijG7x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-thijnvge6LkijG7x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-thijnvge6LkijG7x .marker.cross{stroke:#333333;}#mermaid-svg-thijnvge6LkijG7x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-thijnvge6LkijG7x p{margin:0;}#mermaid-svg-thijnvge6LkijG7x .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-thijnvge6LkijG7x .cluster-label text{fill:#333;}#mermaid-svg-thijnvge6LkijG7x .cluster-label span{color:#333;}#mermaid-svg-thijnvge6LkijG7x .cluster-label span p{background-color:transparent;}#mermaid-svg-thijnvge6LkijG7x .label text,#mermaid-svg-thijnvge6LkijG7x span{fill:#333;color:#333;}#mermaid-svg-thijnvge6LkijG7x .node rect,#mermaid-svg-thijnvge6LkijG7x .node circle,#mermaid-svg-thijnvge6LkijG7x .node ellipse,#mermaid-svg-thijnvge6LkijG7x .node polygon,#mermaid-svg-thijnvge6LkijG7x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-thijnvge6LkijG7x .rough-node .label text,#mermaid-svg-thijnvge6LkijG7x .node .label text,#mermaid-svg-thijnvge6LkijG7x .image-shape .label,#mermaid-svg-thijnvge6LkijG7x .icon-shape .label{text-anchor:middle;}#mermaid-svg-thijnvge6LkijG7x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-thijnvge6LkijG7x .rough-node .label,#mermaid-svg-thijnvge6LkijG7x .node .label,#mermaid-svg-thijnvge6LkijG7x .image-shape .label,#mermaid-svg-thijnvge6LkijG7x .icon-shape .label{text-align:center;}#mermaid-svg-thijnvge6LkijG7x .node.clickable{cursor:pointer;}#mermaid-svg-thijnvge6LkijG7x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-thijnvge6LkijG7x .arrowheadPath{fill:#333333;}#mermaid-svg-thijnvge6LkijG7x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-thijnvge6LkijG7x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-thijnvge6LkijG7x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-thijnvge6LkijG7x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-thijnvge6LkijG7x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-thijnvge6LkijG7x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-thijnvge6LkijG7x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-thijnvge6LkijG7x .cluster text{fill:#333;}#mermaid-svg-thijnvge6LkijG7x .cluster span{color:#333;}#mermaid-svg-thijnvge6LkijG7x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-thijnvge6LkijG7x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-thijnvge6LkijG7x rect.text{fill:none;stroke-width:0;}#mermaid-svg-thijnvge6LkijG7x .icon-shape,#mermaid-svg-thijnvge6LkijG7x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-thijnvge6LkijG7x .icon-shape p,#mermaid-svg-thijnvge6LkijG7x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-thijnvge6LkijG7x .icon-shape .label rect,#mermaid-svg-thijnvge6LkijG7x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-thijnvge6LkijG7x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-thijnvge6LkijG7x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-thijnvge6LkijG7x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
make(\[\]int, 3, 10)
makeslice(int类型, len=3, cap=10)
计算 mem = 10 * 8 = 80 bytes
overflow || mem > maxAlloc?
panic
mallocgc(80, int类型, true)
返回底层数组指针
构造 slice{array:指针, len:3, cap:10}
makeslicecopy------append 触发的创建+拷贝:
源码位置:runtime/slice.go:28
go
func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
var tomem, copymem uintptr
if uintptr(tolen) > uintptr(fromlen) {
tomem, overflow = math.MulUintptr(et.Size_, uintptr(tolen))
copymem = et.Size_ * uintptr(fromlen) // 只拷贝 fromlen 个元素
} else {
tomem = et.Size_ * uintptr(tolen)
copymem = tomem // 目标更小,全部拷贝
}
var to unsafe.Pointer
if !et.Pointers() {
// 元素无指针:不需要 GC 扫描
to = mallocgc(tomem, nil, false)
if copymem < tomem {
// 多余部分清零
memclrNoHeapPointers(add(to, copymem), tomem-copymem)
}
} else {
// 元素有指针:需要 GC 扫描
to = mallocgc(tomem, et, true) // true = 零初始化
}
memmove(to, from, copymem) // 拷贝旧数据
return to
}
2.3 growslice------扩容核心算法
源码位置:runtime/slice.go:126
这是 slice 最核心的函数,决定了 append 时的扩容策略:
go
func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
oldLen := newLen - num // 原始长度
// ★ 零大小元素的特殊处理
if et.Size_ == 0 {
// append 不应创建 nil 指针但非零 len 的 slice
return slice{unsafe.Pointer(&zerobase), newLen, newLen}
}
// ★★★ 核心扩容策略 ★★★
newcap := nextslicecap(newLen, oldCap)
// ★ 内存对齐优化(根据元素大小分4种情况)
var overflow bool
var lenmem, newlenmem, capmem uintptr
noscan := !et.Pointers()
switch {
case et.Size_ == 1:
// byte 类型:无需乘法
lenmem = uintptr(oldLen)
newlenmem = uintptr(newLen)
capmem = roundupsize(uintptr(newcap), noscan) // ★ 对齐到 size class
newcap = int(capmem) // newcap 可能因对齐而增大
case et.Size_ == goarch.PtrSize:
// 指针大小(8字节 on 64-bit):用位移代替乘除
lenmem = uintptr(oldLen) * goarch.PtrSize
newlenmem = uintptr(newLen) * goarch.PtrSize
capmem = roundupsize(uintptr(newcap)*goarch.PtrSize, noscan)
newcap = int(capmem / goarch.PtrSize)
case isPowerOfTwo(et.Size_):
// 2的幂次大小:用位移代替乘除
var shift uintptr
shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63
lenmem = uintptr(oldLen) << shift
newlenmem = uintptr(newLen) << shift
capmem = roundupsize(uintptr(newcap)<<shift, noscan)
newcap = int(capmem >> shift)
capmem = uintptr(newcap) << shift
default:
// 通用情况:乘法
lenmem = uintptr(oldLen) * et.Size_
newlenmem = uintptr(newLen) * et.Size_
capmem, overflow = math.MulUintptr(et.Size_, uintptr(newcap))
capmem = roundupsize(capmem, noscan)
newcap = int(capmem / et.Size_)
capmem = uintptr(newcap) * et.Size_
}
// ★ 分配新底层数组
var p unsafe.Pointer
if !et.Pointers() {
p = mallocgc(capmem, nil, false) // 无需 GC 扫描
memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) // 清零多余部分
} else {
p = mallocgc(capmem, et, true) // 需要 GC 扫描 + 零初始化
}
// ★ 拷贝旧数据到新数组
memmove(p, oldPtr, lenmem)
return slice{p, newLen, newcap}
}
2.4 nextslicecap------扩容策略详解
源码位置:runtime/slice.go:231
go
func nextslicecap(newLen, oldCap int) int {
newcap := oldCap
doublecap := newcap + newcap // 2 * oldCap
// 规则1: 如果新长度 > 2倍旧容量 → 直接用新长度
if newLen > doublecap {
return newLen
}
// 规则2: 旧容量 < 256 → 2倍增长
const threshold = 256
if oldCap < threshold {
return doublecap
}
// 规则3: 旧容量 ≥ 256 → 1.25倍增长(平滑过渡)
for {
// newcap += (newcap + 3*threshold) / 4
// 等价于: newcap = newcap * 1.25 + 192(近似)
newcap += (newcap + 3*threshold) >> 2
if uint(newcap) >= uint(newLen) {
break
}
}
// 溢出保护
if newcap <= 0 {
return newLen
}
return newcap
}
扩容策略三规则总结:
规则1: newLen > 2*oldCap → newcap = newLen (一次性分配足够)
规则2: oldCap < 256 → newcap = 2*oldCap (小切片翻倍)
规则3: oldCap ≥ 256 → newcap ≈ 1.25*oldCap(大切片1.25倍)
过渡示例:
oldCap=128, newLen=200 → 200 > 256? No → 128 < 256? Yes → newcap=256 (2x)
oldCap=256, newLen=300 → 300 > 512? No → 256 < 256? No → newcap=256+(256+768)/4=256+256=512
oldCap=512, newLen=600 → 600 > 1024? No → 512 < 256? No → 512+(512+768)/4=512+320=832
#mermaid-svg-4Fvmnxl47fCenh5m{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4Fvmnxl47fCenh5m .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4Fvmnxl47fCenh5m .error-icon{fill:#552222;}#mermaid-svg-4Fvmnxl47fCenh5m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4Fvmnxl47fCenh5m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4Fvmnxl47fCenh5m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4Fvmnxl47fCenh5m .marker.cross{stroke:#333333;}#mermaid-svg-4Fvmnxl47fCenh5m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4Fvmnxl47fCenh5m p{margin:0;}#mermaid-svg-4Fvmnxl47fCenh5m .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4Fvmnxl47fCenh5m .cluster-label text{fill:#333;}#mermaid-svg-4Fvmnxl47fCenh5m .cluster-label span{color:#333;}#mermaid-svg-4Fvmnxl47fCenh5m .cluster-label span p{background-color:transparent;}#mermaid-svg-4Fvmnxl47fCenh5m .label text,#mermaid-svg-4Fvmnxl47fCenh5m span{fill:#333;color:#333;}#mermaid-svg-4Fvmnxl47fCenh5m .node rect,#mermaid-svg-4Fvmnxl47fCenh5m .node circle,#mermaid-svg-4Fvmnxl47fCenh5m .node ellipse,#mermaid-svg-4Fvmnxl47fCenh5m .node polygon,#mermaid-svg-4Fvmnxl47fCenh5m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4Fvmnxl47fCenh5m .rough-node .label text,#mermaid-svg-4Fvmnxl47fCenh5m .node .label text,#mermaid-svg-4Fvmnxl47fCenh5m .image-shape .label,#mermaid-svg-4Fvmnxl47fCenh5m .icon-shape .label{text-anchor:middle;}#mermaid-svg-4Fvmnxl47fCenh5m .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4Fvmnxl47fCenh5m .rough-node .label,#mermaid-svg-4Fvmnxl47fCenh5m .node .label,#mermaid-svg-4Fvmnxl47fCenh5m .image-shape .label,#mermaid-svg-4Fvmnxl47fCenh5m .icon-shape .label{text-align:center;}#mermaid-svg-4Fvmnxl47fCenh5m .node.clickable{cursor:pointer;}#mermaid-svg-4Fvmnxl47fCenh5m .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4Fvmnxl47fCenh5m .arrowheadPath{fill:#333333;}#mermaid-svg-4Fvmnxl47fCenh5m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4Fvmnxl47fCenh5m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4Fvmnxl47fCenh5m .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4Fvmnxl47fCenh5m .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4Fvmnxl47fCenh5m .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4Fvmnxl47fCenh5m .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4Fvmnxl47fCenh5m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4Fvmnxl47fCenh5m .cluster text{fill:#333;}#mermaid-svg-4Fvmnxl47fCenh5m .cluster span{color:#333;}#mermaid-svg-4Fvmnxl47fCenh5m div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4Fvmnxl47fCenh5m .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4Fvmnxl47fCenh5m rect.text{fill:none;stroke-width:0;}#mermaid-svg-4Fvmnxl47fCenh5m .icon-shape,#mermaid-svg-4Fvmnxl47fCenh5m .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4Fvmnxl47fCenh5m .icon-shape p,#mermaid-svg-4Fvmnxl47fCenh5m .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4Fvmnxl47fCenh5m .icon-shape .label rect,#mermaid-svg-4Fvmnxl47fCenh5m .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4Fvmnxl47fCenh5m .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4Fvmnxl47fCenh5m .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4Fvmnxl47fCenh5m :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 扩容策略
128→256
64→128
256→512→832
512→832→1184
容量跳跃
oldCap < 256
2x 增长
快速翻倍
oldCap ≥ 256
≈1.25x 增长
渐进增长
newLen > 2×oldCap
直接用 newLen
一次性满足
roundupsize------内存对齐到 size class:
go
// roundupsize 将申请大小向上对齐到 mallocgc 的 size class
// 这可能导致实际分配的 cap 比计算值更大
//
// 例: []int, 计算出 newcap=10, capmem=80
// roundupsize(80, noscan=true) → 可能对齐到 96 (下一个 size class)
// 实际 newcap = 96/8 = 12
//
// 这就是为什么 make([]int, 10) 的 cap 可能大于 10 的原因
2.5 append 完整执行流程
编译器将 append 翻译为以下步骤:
源码: s = append(s, x)
编译器生成的伪代码:
1. 如果 cap > len:
// 有空间,直接写入
s[len] = x
s.len = len + 1
2. 如果 cap <= len:
// 需要扩容
newSlice := growslice(s.array, len+1, cap, 1, elemType)
// growslice: 分配新数组 + 拷贝旧数据
newSlice.array[len] = x // 在新数组中写入
newSlice.len = len + 1
s = newSlice // 更新 slice header
3. 多元素 append: s = append(s, x, y, z)
// 同理,但 num=3
#mermaid-svg-a67LUFq83f0pen9q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a67LUFq83f0pen9q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a67LUFq83f0pen9q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a67LUFq83f0pen9q .error-icon{fill:#552222;}#mermaid-svg-a67LUFq83f0pen9q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a67LUFq83f0pen9q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a67LUFq83f0pen9q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a67LUFq83f0pen9q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a67LUFq83f0pen9q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a67LUFq83f0pen9q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a67LUFq83f0pen9q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a67LUFq83f0pen9q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a67LUFq83f0pen9q .marker.cross{stroke:#333333;}#mermaid-svg-a67LUFq83f0pen9q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a67LUFq83f0pen9q p{margin:0;}#mermaid-svg-a67LUFq83f0pen9q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a67LUFq83f0pen9q .cluster-label text{fill:#333;}#mermaid-svg-a67LUFq83f0pen9q .cluster-label span{color:#333;}#mermaid-svg-a67LUFq83f0pen9q .cluster-label span p{background-color:transparent;}#mermaid-svg-a67LUFq83f0pen9q .label text,#mermaid-svg-a67LUFq83f0pen9q span{fill:#333;color:#333;}#mermaid-svg-a67LUFq83f0pen9q .node rect,#mermaid-svg-a67LUFq83f0pen9q .node circle,#mermaid-svg-a67LUFq83f0pen9q .node ellipse,#mermaid-svg-a67LUFq83f0pen9q .node polygon,#mermaid-svg-a67LUFq83f0pen9q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a67LUFq83f0pen9q .rough-node .label text,#mermaid-svg-a67LUFq83f0pen9q .node .label text,#mermaid-svg-a67LUFq83f0pen9q .image-shape .label,#mermaid-svg-a67LUFq83f0pen9q .icon-shape .label{text-anchor:middle;}#mermaid-svg-a67LUFq83f0pen9q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a67LUFq83f0pen9q .rough-node .label,#mermaid-svg-a67LUFq83f0pen9q .node .label,#mermaid-svg-a67LUFq83f0pen9q .image-shape .label,#mermaid-svg-a67LUFq83f0pen9q .icon-shape .label{text-align:center;}#mermaid-svg-a67LUFq83f0pen9q .node.clickable{cursor:pointer;}#mermaid-svg-a67LUFq83f0pen9q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a67LUFq83f0pen9q .arrowheadPath{fill:#333333;}#mermaid-svg-a67LUFq83f0pen9q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a67LUFq83f0pen9q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a67LUFq83f0pen9q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a67LUFq83f0pen9q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a67LUFq83f0pen9q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a67LUFq83f0pen9q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a67LUFq83f0pen9q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a67LUFq83f0pen9q .cluster text{fill:#333;}#mermaid-svg-a67LUFq83f0pen9q .cluster span{color:#333;}#mermaid-svg-a67LUFq83f0pen9q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a67LUFq83f0pen9q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a67LUFq83f0pen9q rect.text{fill:none;stroke-width:0;}#mermaid-svg-a67LUFq83f0pen9q .icon-shape,#mermaid-svg-a67LUFq83f0pen9q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a67LUFq83f0pen9q .icon-shape p,#mermaid-svg-a67LUFq83f0pen9q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a67LUFq83f0pen9q .icon-shape .label rect,#mermaid-svg-a67LUFq83f0pen9q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a67LUFq83f0pen9q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a67LUFq83f0pen9q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a67LUFq83f0pen9q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
s = append(s, elem)
len < cap?
直接写入
s.arraylen = elem
s.len++
★ 不分配内存
growslice 扩容
nextslicecap 计算新容量
roundupsize 内存对齐
mallocgc 分配新数组
memmove 拷贝旧数据
写入新元素
newArraynewLen-1 = elem
更新 slice header
array=newArray, len=newLen, cap=newCap
★ 旧数组可能被 GC 回收
(如果没有其他引用)
2.6 切片表达式------si:j 和 si:j:k
编译器对切片表达式的处理:
s[i:j] → slice{array: s.array + i*elemsize, len: j-i, cap: cap(s)-i}
s[i:j:k] → slice{array: s.array + i*elemsize, len: j-i, cap: k-i}
例: s = []int{0,1,2,3,4,5}, len=6, cap=6
s[1:4] → array=s.array+8, len=3, cap=5 [1,2,3]
s[1:4:5] → array=s.array+8, len=3, cap=4 [1,2,3] 限制cap=4
s[:3] → array=s.array, len=3, cap=6 [0,1,2]
s[2:] → array=s.array+16, len=4, cap=4 [2,3,4,5]
原 slice: [0][1][2][3][4][5]
↑array
│←── len=6 ──→│
│←────── cap=6 ──────→│
s[1:4]: [1][2][3][4][5]
↑s.array+8
│← len=3 →│
│←── cap=5 ──────→│
s[1:4:5]: [1][2][3][4][5]
↑s.array+8
│← len=3 →│
│← cap=4 →│
★ 三索引切片限制了 cap
★ 防止 append 覆盖后续元素
2.7 copy 底层实现
源码位置:runtime/slice.go:310
go
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
if fromLen == 0 || toLen == 0 { return 0 }
n := fromLen
if toLen < n { n = toLen } // 拷贝 min(fromLen, toLen) 个
if width == 0 { return n } // 零大小元素
size := uintptr(n) * width
if size == 1 {
// ★ 1字节的优化:直接赋值
*(*byte)(toPtr) = *(*byte)(fromPtr)
} else {
// ★ 通用:memmove
memmove(toPtr, fromPtr, size)
}
return n
}
copy 的关键语义:
go
// copy 返回实际拷贝的元素数 = min(len(dst), len(src))
n := copy(dst, src)
// 源和目标可以重叠(底层是 memmove 不是 memcpy)
// copy 保证了即使 src 和 dst 有重叠区域,结果也是正确的
3 示例代码逐行解析
3.1 声明与初始化
go
var s []int
// [编译器] s = slice{array: nil, len: 0, cap: 0}
// s == nil → true
// len(s) → 0
// cap(s) → 0
s = make([]int, 3)
// [编译器] 调用 makeslice(int类型, 3, 3)
// [运行时] mallocgc(3*8, int类型, true) → 返回 24 字节数组
// s = slice{array: 指针, len: 3, cap: 3}
s = make([]int, 3, 10)
// [编译器] 调用 makeslice(int类型, 3, 10)
// [运行时] mallocgc(10*8, int类型, true) → 返回 80 字节数组
// s = slice{array: 指针, len: 3, cap: 10}
// s[0..2] = 0 (零初始化)
// s[3..9] 已分配但不可直接访问
s := []int{1, 2, 3}
// [编译器] 在只读数据段生成数组 [1,2,3]
// 然后拷贝到堆上(逃逸分析决定)
// s = slice{array: 堆数组, len: 3, cap: 3}
3.2 append 逐行解析
go
s := make([]int, 0, 2) // len=0, cap=2
s = append(s, 10)
// [编译器] 生成:
// len = 0, cap = 2, len < cap → 有空间
// s.array[0] = 10 // 直接写入
// s.len = 1 // 更新长度
// 结果: s=[10], len=1, cap=2 ✅ 无扩容
s = append(s, 20)
// len = 1, cap = 2, len < cap → 有空间
// s.array[1] = 20
// s.len = 2
// 结果: s=[10,20], len=2, cap=2 ✅ 无扩容
s = append(s, 30)
// len = 2, cap = 2, len == cap → ★ 触发扩容!
// [编译器] 调用 growslice(s.array, 3, 2, 1, int类型)
// oldLen = 3-1 = 2
// newcap = nextslicecap(3, 2):
// doublecap = 4, 3 > 4? No
// oldCap(2) < 256? Yes → newcap = 4
// capmem = roundupsize(4*8, noscan) = roundupsize(32, true)
// → 可能对齐到 32(已经是 size class)
// mallocgc(32, nil, false) → 分配新数组 32 字节
// memmove(新数组, 旧数组, 16) → 拷贝 [10, 20]
// 新数组[2] = 30(由调用者写入)
// s = slice{array: 新指针, len: 3, cap: 4}
// ★ 旧数组(16字节)等待 GC 回收
s = append(s, 40, 50)
// len=3, cap=4, 需要2个空间, 但 cap-len=1 < 2 → ★ 扩容
// growslice(s.array, 5, 4, 2, int类型)
// newcap = nextslicecap(5, 4):
// doublecap = 8, 5 > 8? No
// oldCap(4) < 256? Yes → newcap = 8
// capmem = roundupsize(8*8) = 64
// mallocgc(64, nil, false)
// memmove 拷贝 [10,20,30]
// 新数组[3]=40, [4]=50
// s = slice{array: 新指针, len: 5, cap: 8}
3.3 切片共享底层数组------最经典陷阱
go
arr := [5]int{10, 20, 30, 40, 50}
// 底层数组: [10, 20, 30, 40, 50]
s1 := arr[1:3] // s1=[20,30], len=2, cap=4
s2 := arr[2:4] // s2=[30,40], len=2, cap=3
// ★ s1 和 s2 共享同一个底层数组!
s1[1] = 999
// 修改底层数组: [10, 20, 999, 40, 50]
// s2 也变了!s2=[999, 40]
// ★ 更隐蔽的陷阱:append 覆盖
s1 = append(s1, 100)
// s1 cap=4, len=2, 还有空间
// 底层数组: [10, 20, 999, 100, 50] ← s2[1] 从 40 变成了 100!
// s1 = [20, 999, 100], s2 = [999, 100] ← s2 被意外修改!
共享底层数组图示:
arr: [10][20][30][40][50]
↑arr
│←──── 5 ────→│
s1: [20][30][40][50]
↑s1.array
│←2→│←─4─→│
len cap
s2: [30][40][50]
↑s2.array
│←2→│←3→│
len cap
★ s1[2:4] 和 s2[0:2] 指向相同内存!
★ append(s1, 100) 会覆盖 s2 看到的数据!
3.4 nil slice vs 空 slice
go
var s1 []int // nil slice
s2 := []int{} // 空 slice(非nil)
s3 := make([]int, 0) // 空 slice(非nil)
// s1: slice{array: nil, len: 0, cap: 0} → s1 == nil → true
// s2: slice{array: &zerobase, len: 0, cap: 0} → s2 == nil → false
// s3: slice{array: 指针, len: 0, cap: 0} → s3 == nil → false
// ★ 行为差异:
// - JSON 编码: nil slice → null, 空 slice → []
// - reflect: nil slice 的 Value.IsNil() = true
// - 其他操作 (len/cap/append/for range) 行为完全相同
// ★ 最佳实践:
// 如果要区分"无数据"和"空集合",用 nil slice 表示"无数据"
4 完整使用示例合集
4.1 基础增删改查
go
package main
import "fmt"
func main() {
// 创建
s := make([]int, 0, 10)
// 增(append)
s = append(s, 1, 2, 3) // s=[1,2,3]
s = append(s, 4) // s=[1,2,3,4]
// 批量追加
other := []int{5, 6, 7}
s = append(s, other...) // s=[1,2,3,4,5,6,7]
// 改
s[0] = 100 // s=[100,2,3,4,5,6,7]
// 删(删除索引 i=3)
i := 3
s = append(s[:i], s[i+1:]...) // s=[100,2,3,5,6,7]
// 插入(在索引 i=2 处插入 999)
s = append(s[:2], append([]int{999}, s[2:]...)...)
// s=[100,2,999,3,5,6,7]
// 查
target := 999
for idx, v := range s {
if v == target {
fmt.Printf("found %d at index %d\n", v, idx)
}
}
fmt.Println(s)
}
4.2 删除元素的三种方式
go
// 方式1: 保持顺序(慢,O(n))
s = append(s[:i], s[i+1:]...)
// 方式2: 不保持顺序(快,O(1))
s[i] = s[len(s)-1]
s = s[:len(s)-1]
// 方式3: Go 1.21+ 使用 slices.Delete
s = slices.Delete(s, i, i+1) // 保持顺序
4.3 结构体切片
go
type Item struct {
Name string
Price float64
}
items := []Item{
{"Apple", 1.5},
{"Banana", 0.8},
}
// 修改字段
items[0].Price = 2.0 // ✅ 直接修改
// append
items = append(items, Item{"Cherry", 3.0})
4.4 二维切片
go
// 创建 m×n 的二维切片
m, n := 3, 4
grid := make([][]int, m) // 外层
for i := range grid {
grid[i] = make([]int, n) // 内层
}
// 或者共享底层数组(内存连续,缓存友好)
data := make([]int, m*n) // 一块连续内存
grid := make([][]int, m)
for i := range grid {
grid[i] = data[i*n : (i+1)*n] // 切片引用同一段内存
}
4.5 切片去重
go
func dedup(s []string) []string {
seen := make(map[string]bool)
result := s[:0] // ★ 复用底层数组,零分配
for _, v := range s {
if !seen[v] {
seen[v] = true
result = append(result, v)
}
}
return result
}
5 经典问题与优化
5.1 append 后原 slice 的数据变化
go
s := make([]int, 3, 5) // [0,0,0], len=3, cap=5
s[0], s[1], s[2] = 1, 2, 3
s2 := append(s, 4) // 未扩容,写入 s.array[3]=4
// s = [1,2,3] len=3, cap=5
// s2 = [1,2,3,4] len=4, cap=5
// ★ s 和 s2 共享底层数组!
// ★ s.array[3] 实际已被写入 4,但 s.len=3 所以 s "看不到"
s2[0] = 100
// ★ s[0] 也变成了 100!共享底层数组!
s2 = append(s2, 5, 6, 7) // 触发扩容
// s2 指向新数组,不再与 s 共享
// s 仍然指向旧数组
#mermaid-svg-q9QwtpNgiwPm06tZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q9QwtpNgiwPm06tZ .error-icon{fill:#552222;}#mermaid-svg-q9QwtpNgiwPm06tZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q9QwtpNgiwPm06tZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q9QwtpNgiwPm06tZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q9QwtpNgiwPm06tZ .marker.cross{stroke:#333333;}#mermaid-svg-q9QwtpNgiwPm06tZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q9QwtpNgiwPm06tZ p{margin:0;}#mermaid-svg-q9QwtpNgiwPm06tZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster-label text{fill:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster-label span{color:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster-label span p{background-color:transparent;}#mermaid-svg-q9QwtpNgiwPm06tZ .label text,#mermaid-svg-q9QwtpNgiwPm06tZ span{fill:#333;color:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ .node rect,#mermaid-svg-q9QwtpNgiwPm06tZ .node circle,#mermaid-svg-q9QwtpNgiwPm06tZ .node ellipse,#mermaid-svg-q9QwtpNgiwPm06tZ .node polygon,#mermaid-svg-q9QwtpNgiwPm06tZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q9QwtpNgiwPm06tZ .rough-node .label text,#mermaid-svg-q9QwtpNgiwPm06tZ .node .label text,#mermaid-svg-q9QwtpNgiwPm06tZ .image-shape .label,#mermaid-svg-q9QwtpNgiwPm06tZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-q9QwtpNgiwPm06tZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-q9QwtpNgiwPm06tZ .rough-node .label,#mermaid-svg-q9QwtpNgiwPm06tZ .node .label,#mermaid-svg-q9QwtpNgiwPm06tZ .image-shape .label,#mermaid-svg-q9QwtpNgiwPm06tZ .icon-shape .label{text-align:center;}#mermaid-svg-q9QwtpNgiwPm06tZ .node.clickable{cursor:pointer;}#mermaid-svg-q9QwtpNgiwPm06tZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-q9QwtpNgiwPm06tZ .arrowheadPath{fill:#333333;}#mermaid-svg-q9QwtpNgiwPm06tZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-q9QwtpNgiwPm06tZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-q9QwtpNgiwPm06tZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q9QwtpNgiwPm06tZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-q9QwtpNgiwPm06tZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q9QwtpNgiwPm06tZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster text{fill:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ .cluster span{color:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-q9QwtpNgiwPm06tZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q9QwtpNgiwPm06tZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-q9QwtpNgiwPm06tZ .icon-shape,#mermaid-svg-q9QwtpNgiwPm06tZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q9QwtpNgiwPm06tZ .icon-shape p,#mermaid-svg-q9QwtpNgiwPm06tZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-q9QwtpNgiwPm06tZ .icon-shape .label rect,#mermaid-svg-q9QwtpNgiwPm06tZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q9QwtpNgiwPm06tZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q9QwtpNgiwPm06tZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q9QwtpNgiwPm06tZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 扩容后分离
append 前后共享
再次append
触发扩容
扩容
s: len=3, cap=5
1,2,3,_,_
s2: len=4, cap=5
1,2,3,4,_
s: 旧数组
1,2,3,4,_
s2: 新数组
1,2,3,4,5,6,7,_
5.2 大量 append 的性能优化------预分配
go
// ❌ 不预分配:多次扩容,多次拷贝
var s []int
for i := 0; i < 10000; i++ {
s = append(s, i) // 可能扩容 ~14 次
}
// ✅ 预分配:一次分配,无拷贝
s := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
s = append(s, i) // 从不扩容
}
// ✅ 更好:直接用长度
s := make([]int, 10000)
for i := range s {
s[i] = i
}
扩容次数对比:
不预分配 append 10000 个 int:
0→1→2→4→8→16→32→64→128→256→512→1024→2048→4096→8192→12288
扩容 15 次,拷贝 1+2+4+...+8192 = 16383 个元素
预分配 make([]int, 0, 10000):
扩容 0 次,拷贝 0 个元素
5.3 切片内存泄漏
go
// ❌ 内存泄漏:大切片切小后,底层数组仍持有全部数据
func leak() []int {
big := make([]int, 1000000) // 8MB
// ... 填充数据 ...
return big[:10] // 只返回前10个,但底层 8MB 不会被 GC!
}
// ✅ 修复:拷贝到新切片
func noLeak() []int {
big := make([]int, 1000000)
// ... 填充数据 ...
result := make([]int, 10)
copy(result, big[:10])
return result // big 可以被 GC 回收
}
5.4 三索引切片限制容量
go
arr := [10]int{0,1,2,3,4,5,6,7,8,9}
s := arr[2:5:5] // len=3, cap=3(限制 cap=5-2=3)
// ★ cap 被限制为 3,而不是 8
s = append(s, 100) // len=3, cap=3 → 必须扩容!
// ★ 扩容后 s 指向新数组,不再影响 arr
// 如果不用三索引:
s2 := arr[2:5] // len=3, cap=8
s2 = append(s2, 100) // 不扩容!直接写入 arr[5]=100
// ★ 意外修改了 arr!
5.5 slice 不可比较的原因
go
// s1 == s2 → 编译错误!
// 原因:
// 1. slice 是引用类型,== 比较的是 header 还是内容?语义不明确
// 2. 如果比较内容,深比较 O(n),不符合 == 的 O(1) 预期
// 3. slice 可能有共享底层数组,比较语义复杂
// ✅ 正确的比较方式
func sliceEqual[T comparable](a, b []T) bool {
if len(a) != len(b) { return false }
for i := range a {
if a[i] != b[i] { return false }
}
return true
}
// Go 1.21+: 使用 slices.Equal
slices.Equal(s1, s2)
5.6 切片表达式越界 panic 规则
go
s := make([]int, 5, 10)
// ✅ 合法
_ = s[0:5] // 0 ≤ low ≤ high ≤ cap
_ = s[2:8] // high 可以超过 len,但不超过 cap
_ = s[:5] // 省略 low = 0
_ = s[2:] // 省略 high = len
// ❌ panic
_ = s[0:11] // high > cap → panic: slice bounds out of range
_ = s[6:8] // low > len → panic: slice bounds out of range
_ = s[3:2] // high < low → panic: slice bounds out of range
6 总结:Slice 核心知识图谱
#mermaid-svg-yEIKbPzPkVVtI4mH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yEIKbPzPkVVtI4mH .error-icon{fill:#552222;}#mermaid-svg-yEIKbPzPkVVtI4mH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yEIKbPzPkVVtI4mH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yEIKbPzPkVVtI4mH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yEIKbPzPkVVtI4mH .marker.cross{stroke:#333333;}#mermaid-svg-yEIKbPzPkVVtI4mH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yEIKbPzPkVVtI4mH p{margin:0;}#mermaid-svg-yEIKbPzPkVVtI4mH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster-label text{fill:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster-label span{color:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster-label span p{background-color:transparent;}#mermaid-svg-yEIKbPzPkVVtI4mH .label text,#mermaid-svg-yEIKbPzPkVVtI4mH span{fill:#333;color:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH .node rect,#mermaid-svg-yEIKbPzPkVVtI4mH .node circle,#mermaid-svg-yEIKbPzPkVVtI4mH .node ellipse,#mermaid-svg-yEIKbPzPkVVtI4mH .node polygon,#mermaid-svg-yEIKbPzPkVVtI4mH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yEIKbPzPkVVtI4mH .rough-node .label text,#mermaid-svg-yEIKbPzPkVVtI4mH .node .label text,#mermaid-svg-yEIKbPzPkVVtI4mH .image-shape .label,#mermaid-svg-yEIKbPzPkVVtI4mH .icon-shape .label{text-anchor:middle;}#mermaid-svg-yEIKbPzPkVVtI4mH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yEIKbPzPkVVtI4mH .rough-node .label,#mermaid-svg-yEIKbPzPkVVtI4mH .node .label,#mermaid-svg-yEIKbPzPkVVtI4mH .image-shape .label,#mermaid-svg-yEIKbPzPkVVtI4mH .icon-shape .label{text-align:center;}#mermaid-svg-yEIKbPzPkVVtI4mH .node.clickable{cursor:pointer;}#mermaid-svg-yEIKbPzPkVVtI4mH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yEIKbPzPkVVtI4mH .arrowheadPath{fill:#333333;}#mermaid-svg-yEIKbPzPkVVtI4mH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yEIKbPzPkVVtI4mH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yEIKbPzPkVVtI4mH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEIKbPzPkVVtI4mH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yEIKbPzPkVVtI4mH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEIKbPzPkVVtI4mH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster text{fill:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH .cluster span{color:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yEIKbPzPkVVtI4mH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yEIKbPzPkVVtI4mH rect.text{fill:none;stroke-width:0;}#mermaid-svg-yEIKbPzPkVVtI4mH .icon-shape,#mermaid-svg-yEIKbPzPkVVtI4mH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEIKbPzPkVVtI4mH .icon-shape p,#mermaid-svg-yEIKbPzPkVVtI4mH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yEIKbPzPkVVtI4mH .icon-shape .label rect,#mermaid-svg-yEIKbPzPkVVtI4mH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEIKbPzPkVVtI4mH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yEIKbPzPkVVtI4mH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yEIKbPzPkVVtI4mH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Slice 核心知识
slice header
array+len+cap=24B
创建
make/字面量/数组切片
扩容策略
<256: 2x
≥256: ≈1.25x
append
有空间直接写
无空间growslice
共享底层数组
切片是引用视图
三索引切片
限制cap防覆盖
内存泄漏
大切片切小未copy
nil vs 空
nil=无数据
空=零长度集合
copy
min len
memmove
slice 操作一图总结:
┌───────────────────────────────────────────────────────────┐
│ Slice 操作速查 │
├─────────────┬─────────────────────────────────────────────┤
│ 创建 │ make([]T, len, cap) 字面量 数组[:] │
│ 追加 │ append(s, x...) 可能触发扩容 │
│ 删除(i) │ append(s[:i], s[i+1:]...) O(n) │
│ 插入(i,v) │ append(s[:i], append([]T{v}, s[i:]...)...) │
│ 复制 │ copy(dst, src) min(len) 个 │
│ 切片 │ s[i:j] s[i:j:k] 共享底层数组 │
│ 遍历 │ for i,v := range s │
│ 长度/容量 │ len(s) / cap(s) │
│ 零值 │ nil (array=nil, len=0, cap=0) │
│ 比较 │ 只能与 nil 比较,内容比较用 slices.Equal │
├─────────────┼─────────────────────────────────────────────┤
│ 扩容策略 │ oldCap < 256 → 2× │
│ │ oldCap ≥ 256 → ≈1.25× │
│ │ newLen > 2×oldCap → newLen │
│ │ 实际可能因 roundupsize 更大 │
└─────────────┴─────────────────────────────────────────────┘
源码索引
文件 关键内容 runtime/slice.go:14slice结构体定义(array/len/cap)runtime/slice.go:57makeslice------创建切片内存分配runtime/slice.go:28makeslicecopy------append 触发的创建+拷贝runtime/slice.go:126growslice------扩容核心(分配新数组+拷贝)runtime/slice.go:231nextslicecap------扩容策略(2x/1.25x/newLen)runtime/slice.go:310slicecopy------copy 底层实现runtime/slice.go:345growsliceNoAlias------已知无别名的优化扩容runtime/slice.go:415growsliceBuf------栈缓冲区扩容优化reflect/value.go:2701SliceHeader(已废弃,改用 unsafe.Slice)