Go Slice源码深度解析指南

文章目录

    • [第1章 Slice 数据结构定义](#第1章 Slice 数据结构定义)
      • [1.1 底层结构体](#1.1 底层结构体)
      • [1.2 内存布局](#1.2 内存布局)
      • [1.3 数组 vs Slice 本质区别](#1.3 数组 vs Slice 本质区别)
      • [1.4 Slice 创建方式](#1.4 Slice 创建方式)
    • [第2章 Slice 截取操作原理](#第2章 Slice 截取操作原理)
      • [2.1 截取语法](#2.1 截取语法)
      • [2.2 截取操作内存引用流程](#2.2 截取操作内存引用流程)
      • [2.3 截取操作源码实现](#2.3 截取操作源码实现)
      • [2.4 截取操作的共享问题](#2.4 截取操作的共享问题)
    • [第3章 nil 切片、空切片、零值](#第3章 nil 切片、空切片、零值)
      • [3.1 三者的区别](#3.1 三者的区别)
      • [3.2 代码示例](#3.2 代码示例)
      • [3.3 比较和判断](#3.3 比较和判断)
      • [3.4 zerobase 的作用](#3.4 zerobase 的作用)
    • [第4章 append 扩容完整机制](#第4章 append 扩容完整机制)
      • [4.1 扩容流程图](#4.1 扩容流程图)
      • [4.2 nextslicecap 源码解析](#4.2 nextslicecap 源码解析)
      • [4.3 growslice 完整源码](#4.3 growslice 完整源码)
      • [4.4 roundupsize 内存对齐](#4.4 roundupsize 内存对齐)
      • [4.5 扩容示例](#4.5 扩容示例)
      • [4.6 append 扩容前后内存对比](#4.6 append 扩容前后内存对比)
    • [第5章 copy 函数底层实现](#第5章 copy 函数底层实现)
      • [5.1 copy 源码](#5.1 copy 源码)
      • [5.2 memmove 优化](#5.2 memmove 优化)
      • [5.3 深拷贝 vs 浅拷贝](#5.3 深拷贝 vs 浅拷贝)
    • [第6章 Slice 传参与修改副作用](#第6章 Slice 传参与修改副作用)
      • [6.1 Slice 传参内存图](#6.1 Slice 传参内存图)
      • [6.2 传参本质](#6.2 传参本质)
      • [6.3 返回新 slice 的正确方式](#6.3 返回新 slice 的正确方式)
      • [6.4 传递指针的场景](#6.4 传递指针的场景)
    • [第7章 Slice 内存逃逸与 GC](#第7章 Slice 内存逃逸与 GC)
      • [7.1 逃逸分析](#7.1 逃逸分析)
      • [7.2 GC 对 Slice 的处理](#7.2 GC 对 Slice 的处理)
      • [7.3 内存泄漏陷阱](#7.3 内存泄漏陷阱)
    • [第8章 项目代码实战解析](#第8章 项目代码实战解析)
      • [8.1 strings.Builder 中的 Slice 应用](#8.1 strings.Builder 中的 Slice 应用)
      • [8.2 makeslice 源码分析](#8.2 makeslice 源码分析)
      • [8.3 slicecopy 在标准库中的应用](#8.3 slicecopy 在标准库中的应用)
      • [8.4 扩容实战示例](#8.4 扩容实战示例)
    • [第9章 常见问题与面试题](#第9章 常见问题与面试题)
      • [9.1 常见面试题](#9.1 常见面试题)
      • [9.2 最佳实践](#9.2 最佳实践)
    • 附录:完整源码链接
    • 总结

第1章 Slice 数据结构定义

1.1 底层结构体

Slice 在 runtime 中的定义位于 src/runtime/slice.go

go 复制代码
type slice struct {
    array unsafe.Pointer  // 指向底层数组的指针
    len   int             // 切片长度:当前元素个数
    cap   int             // 切片容量:底层数组从指针位置开始的总空间
}

1.2 内存布局

#mermaid-svg-7d3hhDosLkT16ZjB{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-7d3hhDosLkT16ZjB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7d3hhDosLkT16ZjB .error-icon{fill:#552222;}#mermaid-svg-7d3hhDosLkT16ZjB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7d3hhDosLkT16ZjB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7d3hhDosLkT16ZjB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7d3hhDosLkT16ZjB .marker.cross{stroke:#333333;}#mermaid-svg-7d3hhDosLkT16ZjB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7d3hhDosLkT16ZjB p{margin:0;}#mermaid-svg-7d3hhDosLkT16ZjB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7d3hhDosLkT16ZjB .cluster-label text{fill:#333;}#mermaid-svg-7d3hhDosLkT16ZjB .cluster-label span{color:#333;}#mermaid-svg-7d3hhDosLkT16ZjB .cluster-label span p{background-color:transparent;}#mermaid-svg-7d3hhDosLkT16ZjB .label text,#mermaid-svg-7d3hhDosLkT16ZjB span{fill:#333;color:#333;}#mermaid-svg-7d3hhDosLkT16ZjB .node rect,#mermaid-svg-7d3hhDosLkT16ZjB .node circle,#mermaid-svg-7d3hhDosLkT16ZjB .node ellipse,#mermaid-svg-7d3hhDosLkT16ZjB .node polygon,#mermaid-svg-7d3hhDosLkT16ZjB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7d3hhDosLkT16ZjB .rough-node .label text,#mermaid-svg-7d3hhDosLkT16ZjB .node .label text,#mermaid-svg-7d3hhDosLkT16ZjB .image-shape .label,#mermaid-svg-7d3hhDosLkT16ZjB .icon-shape .label{text-anchor:middle;}#mermaid-svg-7d3hhDosLkT16ZjB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7d3hhDosLkT16ZjB .rough-node .label,#mermaid-svg-7d3hhDosLkT16ZjB .node .label,#mermaid-svg-7d3hhDosLkT16ZjB .image-shape .label,#mermaid-svg-7d3hhDosLkT16ZjB .icon-shape .label{text-align:center;}#mermaid-svg-7d3hhDosLkT16ZjB .node.clickable{cursor:pointer;}#mermaid-svg-7d3hhDosLkT16ZjB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7d3hhDosLkT16ZjB .arrowheadPath{fill:#333333;}#mermaid-svg-7d3hhDosLkT16ZjB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7d3hhDosLkT16ZjB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7d3hhDosLkT16ZjB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7d3hhDosLkT16ZjB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7d3hhDosLkT16ZjB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7d3hhDosLkT16ZjB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7d3hhDosLkT16ZjB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7d3hhDosLkT16ZjB .cluster text{fill:#333;}#mermaid-svg-7d3hhDosLkT16ZjB .cluster span{color:#333;}#mermaid-svg-7d3hhDosLkT16ZjB 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-7d3hhDosLkT16ZjB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7d3hhDosLkT16ZjB rect.text{fill:none;stroke-width:0;}#mermaid-svg-7d3hhDosLkT16ZjB .icon-shape,#mermaid-svg-7d3hhDosLkT16ZjB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7d3hhDosLkT16ZjB .icon-shape p,#mermaid-svg-7d3hhDosLkT16ZjB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7d3hhDosLkT16ZjB .icon-shape rect,#mermaid-svg-7d3hhDosLkT16ZjB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7d3hhDosLkT16ZjB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7d3hhDosLkT16ZjB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7d3hhDosLkT16ZjB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Backing Array
Slice Header (24 bytes on 64-bit)
array unsafe.Pointer

8 bytes
len int

8 bytes
cap int

8 bytes
Element 0
Element 1
Element 2
Element 3
Element 4
Element 5

内存布局说明

  • Slice Header 是一个 3 字段结构体,在 64 位系统上占 24 字节
  • array:指向底层数组第一个元素的指针
  • len :当前可以读写的元素个数,访问 s[len] 会 panic
  • cap:从 array 指针位置开始,底层数组剩余的空间大小

1.3 数组 vs Slice 本质区别

特性 数组 Slice
类型表示 [5]int(长度是类型的一部分) []int(长度不参与类型)
内存结构 直接存储元素 存储指针+len+cap
赋值行为 值拷贝(复制所有元素) 仅拷贝 Slice Header(共享底层数组)
长度 固定,不可变 动态,可通过 append 增长
函数参数 值传递(有拷贝开销) 值传递(仅 24 字节,共享数组)

1.4 Slice 创建方式

go 复制代码
// 方式1:直接声明(零值)
var s []int  // s == nil

// 方式2:make 创建
s := make([]int, 5)      // len=5, cap=5,元素零值化
s := make([]int, 5, 10)  // len=5, cap=10

// 方式3:字面量
s := []int{1, 2, 3}  // len=3, cap=3

// 方式4:从现有切片/数组截取
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:3]  // len=2, cap=4,指向 arr[1]

第2章 Slice 截取操作原理

2.1 截取语法

go 复制代码
s[i:j]      // 从 i 到 j(不包含 j),len = j-i, cap = original_cap - i
s[i:j:k]    // 从 i 到 j,容量限制到 k,len = j-i, cap = k-i
s[:]        // 完整切片
s[i:]       // 从 i 到末尾
s[:j]       // 从开头到 j

2.2 截取操作内存引用流程

New Slice s1:4 len=3, cap=4 Backing Array Original Slice len=5, cap=5 New Slice s1:4 len=3, cap=4 Backing Array Original Slice len=5, cap=5 #mermaid-svg-Dcpz28fncC5QHRMY{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-Dcpz28fncC5QHRMY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dcpz28fncC5QHRMY .error-icon{fill:#552222;}#mermaid-svg-Dcpz28fncC5QHRMY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dcpz28fncC5QHRMY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dcpz28fncC5QHRMY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dcpz28fncC5QHRMY .marker.cross{stroke:#333333;}#mermaid-svg-Dcpz28fncC5QHRMY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dcpz28fncC5QHRMY p{margin:0;}#mermaid-svg-Dcpz28fncC5QHRMY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dcpz28fncC5QHRMY text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Dcpz28fncC5QHRMY .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Dcpz28fncC5QHRMY .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Dcpz28fncC5QHRMY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Dcpz28fncC5QHRMY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Dcpz28fncC5QHRMY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Dcpz28fncC5QHRMY .sequenceNumber{fill:white;}#mermaid-svg-Dcpz28fncC5QHRMY #sequencenumber{fill:#333;}#mermaid-svg-Dcpz28fncC5QHRMY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Dcpz28fncC5QHRMY .messageText{fill:#333;stroke:none;}#mermaid-svg-Dcpz28fncC5QHRMY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dcpz28fncC5QHRMY .labelText,#mermaid-svg-Dcpz28fncC5QHRMY .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Dcpz28fncC5QHRMY .loopText,#mermaid-svg-Dcpz28fncC5QHRMY .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Dcpz28fncC5QHRMY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Dcpz28fncC5QHRMY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Dcpz28fncC5QHRMY .noteText,#mermaid-svg-Dcpz28fncC5QHRMY .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Dcpz28fncC5QHRMY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dcpz28fncC5QHRMY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dcpz28fncC5QHRMY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dcpz28fncC5QHRMY .actorPopupMenu{position:absolute;}#mermaid-svg-Dcpz28fncC5QHRMY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Dcpz28fncC5QHRMY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dcpz28fncC5QHRMY .actor-man circle,#mermaid-svg-Dcpz28fncC5QHRMY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Dcpz28fncC5QHRMY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0, 1, 2, 3, 4 0, 1, 2, 3, 4 ↑-----↑ new slice view 修改 s0 = 99 会影响原数组 array points to index 0 array points to index 1

关键点:截取操作不会复制数据,只创建新的 slice 结构体指向同一个底层数组。
#mermaid-svg-zScNk2irw63TVcoh{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-zScNk2irw63TVcoh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zScNk2irw63TVcoh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zScNk2irw63TVcoh .error-icon{fill:#552222;}#mermaid-svg-zScNk2irw63TVcoh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zScNk2irw63TVcoh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zScNk2irw63TVcoh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zScNk2irw63TVcoh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zScNk2irw63TVcoh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zScNk2irw63TVcoh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zScNk2irw63TVcoh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zScNk2irw63TVcoh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zScNk2irw63TVcoh .marker.cross{stroke:#333333;}#mermaid-svg-zScNk2irw63TVcoh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zScNk2irw63TVcoh p{margin:0;}#mermaid-svg-zScNk2irw63TVcoh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zScNk2irw63TVcoh .cluster-label text{fill:#333;}#mermaid-svg-zScNk2irw63TVcoh .cluster-label span{color:#333;}#mermaid-svg-zScNk2irw63TVcoh .cluster-label span p{background-color:transparent;}#mermaid-svg-zScNk2irw63TVcoh .label text,#mermaid-svg-zScNk2irw63TVcoh span{fill:#333;color:#333;}#mermaid-svg-zScNk2irw63TVcoh .node rect,#mermaid-svg-zScNk2irw63TVcoh .node circle,#mermaid-svg-zScNk2irw63TVcoh .node ellipse,#mermaid-svg-zScNk2irw63TVcoh .node polygon,#mermaid-svg-zScNk2irw63TVcoh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zScNk2irw63TVcoh .rough-node .label text,#mermaid-svg-zScNk2irw63TVcoh .node .label text,#mermaid-svg-zScNk2irw63TVcoh .image-shape .label,#mermaid-svg-zScNk2irw63TVcoh .icon-shape .label{text-anchor:middle;}#mermaid-svg-zScNk2irw63TVcoh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zScNk2irw63TVcoh .rough-node .label,#mermaid-svg-zScNk2irw63TVcoh .node .label,#mermaid-svg-zScNk2irw63TVcoh .image-shape .label,#mermaid-svg-zScNk2irw63TVcoh .icon-shape .label{text-align:center;}#mermaid-svg-zScNk2irw63TVcoh .node.clickable{cursor:pointer;}#mermaid-svg-zScNk2irw63TVcoh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zScNk2irw63TVcoh .arrowheadPath{fill:#333333;}#mermaid-svg-zScNk2irw63TVcoh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zScNk2irw63TVcoh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zScNk2irw63TVcoh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zScNk2irw63TVcoh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zScNk2irw63TVcoh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zScNk2irw63TVcoh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zScNk2irw63TVcoh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zScNk2irw63TVcoh .cluster text{fill:#333;}#mermaid-svg-zScNk2irw63TVcoh .cluster span{color:#333;}#mermaid-svg-zScNk2irw63TVcoh 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-zScNk2irw63TVcoh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zScNk2irw63TVcoh rect.text{fill:none;stroke-width:0;}#mermaid-svg-zScNk2irw63TVcoh .icon-shape,#mermaid-svg-zScNk2irw63TVcoh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zScNk2irw63TVcoh .icon-shape p,#mermaid-svg-zScNk2irw63TVcoh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zScNk2irw63TVcoh .icon-shape rect,#mermaid-svg-zScNk2irw63TVcoh .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zScNk2irw63TVcoh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zScNk2irw63TVcoh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zScNk2irw63TVcoh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 子切片 s2 = s2:4:5
子切片 s1 = s1:4
底层数组 0x1000
原切片 s = 0,1,2,3,4,5 len=6, cap=6
array ptr

0x1000
len=6
cap=6
0
1
2
3
4
5
array ptr

0x1008
len=3
cap=5
array ptr

0x1010
len=2
cap=3

2.3 截取操作源码实现

截取操作在编译时直接转换为指针运算,不调用 runtime 函数:

go 复制代码
// 编译后的伪代码
func slicePtr(s slice, i, j int) slice {
    if i < 0 || j < i || j > s.cap {
        panic("slice index out of range")
    }
    return slice{
        array: add(s.array, uintptr(i)*elemSize),
        len:   j - i,
        cap:   s.cap - i,
    }
}

2.4 截取操作的共享问题

go 复制代码
original := []int{1, 2, 3, 4, 5}
s1 := original[1:3]     // [2, 3], len=2, cap=4
s2 := original[1:3:3]   // [2, 3], len=2, cap=2  (容量受限)

s1[0] = 999             // 修改会影响 original
fmt.Println(original)   // [1, 999, 3, 4, 5]

// s1 可以继续 append,因为 cap=4
s1 = append(s1, 100)    // 不会扩容,直接修改 original
fmt.Println(original)   // [1, 999, 3, 100, 5]

// s2 append 会触发扩容,因为 cap=2
s2 = append(s2, 200)    // 分配新数组,不再共享 original

第3章 nil 切片、空切片、零值

3.1 三者的区别

#mermaid-svg-r4tLLLFTtYFWBX4O{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-r4tLLLFTtYFWBX4O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r4tLLLFTtYFWBX4O .error-icon{fill:#552222;}#mermaid-svg-r4tLLLFTtYFWBX4O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r4tLLLFTtYFWBX4O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r4tLLLFTtYFWBX4O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r4tLLLFTtYFWBX4O .marker.cross{stroke:#333333;}#mermaid-svg-r4tLLLFTtYFWBX4O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r4tLLLFTtYFWBX4O p{margin:0;}#mermaid-svg-r4tLLLFTtYFWBX4O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster-label text{fill:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster-label span{color:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster-label span p{background-color:transparent;}#mermaid-svg-r4tLLLFTtYFWBX4O .label text,#mermaid-svg-r4tLLLFTtYFWBX4O span{fill:#333;color:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O .node rect,#mermaid-svg-r4tLLLFTtYFWBX4O .node circle,#mermaid-svg-r4tLLLFTtYFWBX4O .node ellipse,#mermaid-svg-r4tLLLFTtYFWBX4O .node polygon,#mermaid-svg-r4tLLLFTtYFWBX4O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r4tLLLFTtYFWBX4O .rough-node .label text,#mermaid-svg-r4tLLLFTtYFWBX4O .node .label text,#mermaid-svg-r4tLLLFTtYFWBX4O .image-shape .label,#mermaid-svg-r4tLLLFTtYFWBX4O .icon-shape .label{text-anchor:middle;}#mermaid-svg-r4tLLLFTtYFWBX4O .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r4tLLLFTtYFWBX4O .rough-node .label,#mermaid-svg-r4tLLLFTtYFWBX4O .node .label,#mermaid-svg-r4tLLLFTtYFWBX4O .image-shape .label,#mermaid-svg-r4tLLLFTtYFWBX4O .icon-shape .label{text-align:center;}#mermaid-svg-r4tLLLFTtYFWBX4O .node.clickable{cursor:pointer;}#mermaid-svg-r4tLLLFTtYFWBX4O .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r4tLLLFTtYFWBX4O .arrowheadPath{fill:#333333;}#mermaid-svg-r4tLLLFTtYFWBX4O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r4tLLLFTtYFWBX4O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r4tLLLFTtYFWBX4O .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r4tLLLFTtYFWBX4O .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r4tLLLFTtYFWBX4O .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r4tLLLFTtYFWBX4O .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster text{fill:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O .cluster span{color:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O 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-r4tLLLFTtYFWBX4O .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r4tLLLFTtYFWBX4O rect.text{fill:none;stroke-width:0;}#mermaid-svg-r4tLLLFTtYFWBX4O .icon-shape,#mermaid-svg-r4tLLLFTtYFWBX4O .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r4tLLLFTtYFWBX4O .icon-shape p,#mermaid-svg-r4tLLLFTtYFWBX4O .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r4tLLLFTtYFWBX4O .icon-shape rect,#mermaid-svg-r4tLLLFTtYFWBX4O .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r4tLLLFTtYFWBX4O .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r4tLLLFTtYFWBX4O .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r4tLLLFTtYFWBX4O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} zerobase (global)
empty slice (literal)
empty slice (make)
nil slice
array: nil
len: 0
cap: 0
array: &zerobase
len: 0
cap: 0
array: &zerobase
len: 0
cap: 0
special address

for zero-size allocs

3.2 代码示例

go 复制代码
// nil 切片
var s1 []int
// s1.array = nil
// s1.len = 0
// s1.cap = 0

// 空切片(make)
s2 := make([]int, 0)
// s2.array = &zerobase
// s2.len = 0
// s2.cap = 0

// 空切片(字面量)
s3 := []int{}
// s3.array = &zerobase
// s3.len = 0
// s3.cap = 0

// 零值
s4 := make([]int, 3)
// s4.array = 指向分配的数组
// s4.len = 3
// s4.cap = 3
// 元素:[0, 0, 0]

3.3 比较和判断

go 复制代码
var s1 []int
s2 := []int{}

fmt.Println(s1 == nil)  // true
fmt.Println(s2 == nil)  // false

fmt.Println(len(s1))    // 0
fmt.Println(len(s2))    // 0

// 推荐:使用 len(s) == 0 判断是否为空
if len(s) == 0 {
    // 处理空切片
}

3.4 zerobase 的作用

zerobase 是 runtime 中的一个特殊全局变量,用于所有零大小内存分配:

go 复制代码
// src/runtime/malloc.go
var zerobase uintptr
  • 所有零大小的分配(make([]T, 0), new(struct{}) 等)都返回 &zerobase
  • 避免为大量零大小对象分配独立内存
  • nil 切片的 array 字段是真正的 nil,不是 &zerobase

第4章 append 扩容完整机制


append 是可变参数函数,支持:

  • s = append(s, elem) - 追加单个元素
  • s = append(s, e1, e2, e3) - 追加多个元素
  • s = append(s, anotherSlice...) - 追加整个切片
    注意:返回新的 slice,可能复用原数组也可能分配新数组。

4.1 扩容流程图

#mermaid-svg-7l0S7kAt9XbShISB{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-7l0S7kAt9XbShISB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7l0S7kAt9XbShISB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7l0S7kAt9XbShISB .error-icon{fill:#552222;}#mermaid-svg-7l0S7kAt9XbShISB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7l0S7kAt9XbShISB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7l0S7kAt9XbShISB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7l0S7kAt9XbShISB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7l0S7kAt9XbShISB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7l0S7kAt9XbShISB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7l0S7kAt9XbShISB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7l0S7kAt9XbShISB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7l0S7kAt9XbShISB .marker.cross{stroke:#333333;}#mermaid-svg-7l0S7kAt9XbShISB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7l0S7kAt9XbShISB p{margin:0;}#mermaid-svg-7l0S7kAt9XbShISB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7l0S7kAt9XbShISB .cluster-label text{fill:#333;}#mermaid-svg-7l0S7kAt9XbShISB .cluster-label span{color:#333;}#mermaid-svg-7l0S7kAt9XbShISB .cluster-label span p{background-color:transparent;}#mermaid-svg-7l0S7kAt9XbShISB .label text,#mermaid-svg-7l0S7kAt9XbShISB span{fill:#333;color:#333;}#mermaid-svg-7l0S7kAt9XbShISB .node rect,#mermaid-svg-7l0S7kAt9XbShISB .node circle,#mermaid-svg-7l0S7kAt9XbShISB .node ellipse,#mermaid-svg-7l0S7kAt9XbShISB .node polygon,#mermaid-svg-7l0S7kAt9XbShISB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7l0S7kAt9XbShISB .rough-node .label text,#mermaid-svg-7l0S7kAt9XbShISB .node .label text,#mermaid-svg-7l0S7kAt9XbShISB .image-shape .label,#mermaid-svg-7l0S7kAt9XbShISB .icon-shape .label{text-anchor:middle;}#mermaid-svg-7l0S7kAt9XbShISB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7l0S7kAt9XbShISB .rough-node .label,#mermaid-svg-7l0S7kAt9XbShISB .node .label,#mermaid-svg-7l0S7kAt9XbShISB .image-shape .label,#mermaid-svg-7l0S7kAt9XbShISB .icon-shape .label{text-align:center;}#mermaid-svg-7l0S7kAt9XbShISB .node.clickable{cursor:pointer;}#mermaid-svg-7l0S7kAt9XbShISB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7l0S7kAt9XbShISB .arrowheadPath{fill:#333333;}#mermaid-svg-7l0S7kAt9XbShISB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7l0S7kAt9XbShISB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7l0S7kAt9XbShISB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7l0S7kAt9XbShISB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7l0S7kAt9XbShISB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7l0S7kAt9XbShISB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7l0S7kAt9XbShISB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7l0S7kAt9XbShISB .cluster text{fill:#333;}#mermaid-svg-7l0S7kAt9XbShISB .cluster span{color:#333;}#mermaid-svg-7l0S7kAt9XbShISB 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-7l0S7kAt9XbShISB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7l0S7kAt9XbShISB rect.text{fill:none;stroke-width:0;}#mermaid-svg-7l0S7kAt9XbShISB .icon-shape,#mermaid-svg-7l0S7kAt9XbShISB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7l0S7kAt9XbShISB .icon-shape p,#mermaid-svg-7l0S7kAt9XbShISB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7l0S7kAt9XbShISB .icon-shape rect,#mermaid-svg-7l0S7kAt9XbShISB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7l0S7kAt9XbShISB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7l0S7kAt9XbShISB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7l0S7kAt9XbShISB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是




调用 append
len + num <= cap?
不扩容,直接写入
调用 growslice
计算 newcap
oldCap < 256?
newcap = 2 * oldCap
newcap = oldCap + (oldCap + 3*256)/4
newcap 溢出或小于需要?
newcap = newLen
内存对齐: roundupsize
分配新数组
复制旧元素
返回新 slice header


容量不足触发 growslice 完整调用时序图
"memmove" "mallocgc" "growslice" "编译器生成" "用户代码" "memmove" "mallocgc" "growslice" "编译器生成" "用户代码" #mermaid-svg-k5Y64jBJN0Qqf90M{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-k5Y64jBJN0Qqf90M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k5Y64jBJN0Qqf90M .error-icon{fill:#552222;}#mermaid-svg-k5Y64jBJN0Qqf90M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k5Y64jBJN0Qqf90M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k5Y64jBJN0Qqf90M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k5Y64jBJN0Qqf90M .marker.cross{stroke:#333333;}#mermaid-svg-k5Y64jBJN0Qqf90M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k5Y64jBJN0Qqf90M p{margin:0;}#mermaid-svg-k5Y64jBJN0Qqf90M .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-k5Y64jBJN0Qqf90M text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-k5Y64jBJN0Qqf90M .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-k5Y64jBJN0Qqf90M .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-k5Y64jBJN0Qqf90M #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-k5Y64jBJN0Qqf90M .sequenceNumber{fill:white;}#mermaid-svg-k5Y64jBJN0Qqf90M #sequencenumber{fill:#333;}#mermaid-svg-k5Y64jBJN0Qqf90M #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-k5Y64jBJN0Qqf90M .messageText{fill:#333;stroke:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-k5Y64jBJN0Qqf90M .labelText,#mermaid-svg-k5Y64jBJN0Qqf90M .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .loopText,#mermaid-svg-k5Y64jBJN0Qqf90M .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-k5Y64jBJN0Qqf90M .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-k5Y64jBJN0Qqf90M .noteText,#mermaid-svg-k5Y64jBJN0Qqf90M .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-k5Y64jBJN0Qqf90M .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-k5Y64jBJN0Qqf90M .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-k5Y64jBJN0Qqf90M .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-k5Y64jBJN0Qqf90M .actorPopupMenu{position:absolute;}#mermaid-svg-k5Y64jBJN0Qqf90M .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-k5Y64jBJN0Qqf90M .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-k5Y64jBJN0Qqf90M .actor-man circle,#mermaid-svg-k5Y64jBJN0Qqf90M line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-k5Y64jBJN0Qqf90M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt 容量足够 容量不足 s = append(s, e1, e2) 检查 len+2 <= cap? slen=e1, slen+1=e2 s.len += 2 growslice(oldPtr, len+2, cap, 2, et) nextslicecap 计算新容量 mallocgc 分配新内存 返回新内存指针 memmove 复制旧数据 完成复制 组装新 slice 返回新 slice{array,len+2,newcap} 写入新元素 e1,e2 返回新 slice

4.2 nextslicecap 源码解析

核心扩容算法位于 src/runtime/slice.go

go 复制代码
func nextslicecap(newLen, oldCap int) int {
    newcap := oldCap
    doublecap := newcap + newcap
    
    // 规则1:如果需要的长度超过双倍容量,直接使用需要的长度
    if newLen > doublecap {
        return newLen
    }

    const threshold = 256
    
    // 规则2:小容量(<256)时,直接翻倍
    if oldCap < threshold {
        return doublecap
    }
    
    // 规则3:大容量时,渐进式增长 1.25x
    for {
        // newcap += newcap/4 + 192
        // 等价于: newcap = 1.25 * newcap + 192
        newcap += (newcap + 3*threshold) >> 2

        // 检查是否满足需求或溢出
        if uint(newcap) >= uint(newLen) {
            break
        }
    }

    // 溢出处理
    if newcap <= 0 {
        return newLen
    }
    return newcap
}

4.3 growslice 完整源码


growslice设计平衡的三个目标

  1. 减少扩容次数(容量增长不能太慢)
  2. 减少内存浪费(容量增长不能太快)
  3. 内存对齐(分配适合 size class 的大小)

为什么这么设计?

小切片(<256)快速增长:减少扩容次数,小切片扩容成本低

大切片(≥256)慢速增长:避免内存浪费,大切片内存开销大

平滑过渡公式:newcap += (newcap + 3*threshold) >> 2 让增长率从 2x 自然过渡到 1.25x


go 复制代码
func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
    oldLen := newLen - num
    
    // 检查元素大小为 0 的特殊情况
    if et.Size_ == 0 {
        return slice{unsafe.Pointer(&zerobase), newLen, newLen}
    }

    // 步骤1:计算新容量
    newcap := nextslicecap(newLen, oldCap)

    var overflow bool
    var lenmem, newlenmem, capmem uintptr
    noscan := !et.Pointers()
    
    // 步骤2:对常见元素大小进行特殊优化
    switch {
    case et.Size_ == 1:
        lenmem = uintptr(oldLen)
        newlenmem = uintptr(newLen)
        capmem = roundupsize(uintptr(newcap), noscan)
        overflow = uintptr(newcap) > maxAlloc
        newcap = int(capmem)
    case et.Size_ == goarch.PtrSize:
        lenmem = uintptr(oldLen) * goarch.PtrSize
        newlenmem = uintptr(newLen) * goarch.PtrSize
        capmem = roundupsize(uintptr(newcap)*goarch.PtrSize, noscan)
        overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
        newcap = int(capmem / goarch.PtrSize)
    case isPowerOfTwo(et.Size_):
        var shift uintptr
        if goarch.PtrSize == 8 {
            shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63
        } else {
            shift = uintptr(sys.TrailingZeros32(uint32(et.Size_))) & 31
        }
        lenmem = uintptr(oldLen) << shift
        newlenmem = uintptr(newLen) << shift
        capmem = roundupsize(uintptr(newcap)<<shift, noscan)
        overflow = uintptr(newcap) > (maxAlloc >> shift)
        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_
    }

    // 步骤3:检查溢出
    if overflow || capmem > maxAlloc {
        panic(errorString("growslice: len out of range"))
    }

    // 步骤4:分配新内存
    var p unsafe.Pointer
    if !et.Pointers() {
        // 不含指针的类型:不清零未使用部分
        p = mallocgc(capmem, nil, false)
        memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
    } else {
        // 含指针的类型:需要清零,避免 GC 扫描垃圾
        p = mallocgc(capmem, et, true)
        if lenmem > 0 && writeBarrier.enabled {
            // 批量写屏障
            bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldPtr), lenmem-et.Size_+et.PtrBytes, et)
        }
    }
    
    // 步骤5:复制旧元素
    memmove(p, oldPtr, lenmem)

    return slice{p, newLen, newcap}
}

4.4 roundupsize 内存对齐

扩容后会调用 roundupsize 进行内存对齐,实际分配的大小会匹配 size class:

go 复制代码
func roundupsize(size uintptr, noscan bool) uintptr {
    if size < _MaxSmallSize {
        // 小对象:对齐到 size class
        if noscan {
            return uintptr(class_to_size[size_to_class8[divRoundUp(size, 8)]])
        }
        return uintptr(class_to_size[size_to_class128[divRoundUp(size, 128)]])
    }
    // 大对象:页对齐
    return alignUp(size, _PageSize)
}

4.5 扩容示例

go 复制代码
// 示例1:小切片扩容
s := make([]int, 0, 100)
fmt.Println(cap(s))  // 100
s = append(s, make([]int, 200)...)
fmt.Println(cap(s))  // 200 (newLen > 2*oldCap)

// 示例2:阈值临界点
s := make([]int, 256)
s = append(s, 1)
fmt.Println(cap(s))  // 256 + (256 + 768)/4 = 256 + 256 = 512

// 示例3:渐进式增长
s := make([]int, 1000)
fmt.Println(cap(s))  // 1000
s = append(s, make([]int, 201)...)  // 超出 cap
fmt.Println(cap(s))  // 1000 + (1000 + 768)/4 = 1000 + 442 = 1442
                      // 然后 roundupsize 对齐到实际 size class

4.6 append 扩容前后内存对比

#mermaid-svg-QgS4HfoUypbNzAA3{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-QgS4HfoUypbNzAA3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QgS4HfoUypbNzAA3 .error-icon{fill:#552222;}#mermaid-svg-QgS4HfoUypbNzAA3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QgS4HfoUypbNzAA3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QgS4HfoUypbNzAA3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QgS4HfoUypbNzAA3 .marker.cross{stroke:#333333;}#mermaid-svg-QgS4HfoUypbNzAA3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QgS4HfoUypbNzAA3 p{margin:0;}#mermaid-svg-QgS4HfoUypbNzAA3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster-label text{fill:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster-label span{color:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster-label span p{background-color:transparent;}#mermaid-svg-QgS4HfoUypbNzAA3 .label text,#mermaid-svg-QgS4HfoUypbNzAA3 span{fill:#333;color:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 .node rect,#mermaid-svg-QgS4HfoUypbNzAA3 .node circle,#mermaid-svg-QgS4HfoUypbNzAA3 .node ellipse,#mermaid-svg-QgS4HfoUypbNzAA3 .node polygon,#mermaid-svg-QgS4HfoUypbNzAA3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QgS4HfoUypbNzAA3 .rough-node .label text,#mermaid-svg-QgS4HfoUypbNzAA3 .node .label text,#mermaid-svg-QgS4HfoUypbNzAA3 .image-shape .label,#mermaid-svg-QgS4HfoUypbNzAA3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-QgS4HfoUypbNzAA3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QgS4HfoUypbNzAA3 .rough-node .label,#mermaid-svg-QgS4HfoUypbNzAA3 .node .label,#mermaid-svg-QgS4HfoUypbNzAA3 .image-shape .label,#mermaid-svg-QgS4HfoUypbNzAA3 .icon-shape .label{text-align:center;}#mermaid-svg-QgS4HfoUypbNzAA3 .node.clickable{cursor:pointer;}#mermaid-svg-QgS4HfoUypbNzAA3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QgS4HfoUypbNzAA3 .arrowheadPath{fill:#333333;}#mermaid-svg-QgS4HfoUypbNzAA3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QgS4HfoUypbNzAA3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QgS4HfoUypbNzAA3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QgS4HfoUypbNzAA3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QgS4HfoUypbNzAA3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QgS4HfoUypbNzAA3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster text{fill:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 .cluster span{color:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 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-QgS4HfoUypbNzAA3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QgS4HfoUypbNzAA3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-QgS4HfoUypbNzAA3 .icon-shape,#mermaid-svg-QgS4HfoUypbNzAA3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QgS4HfoUypbNzAA3 .icon-shape p,#mermaid-svg-QgS4HfoUypbNzAA3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QgS4HfoUypbNzAA3 .icon-shape rect,#mermaid-svg-QgS4HfoUypbNzAA3 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QgS4HfoUypbNzAA3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QgS4HfoUypbNzAA3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QgS4HfoUypbNzAA3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} After append (cap=8)
array
0

index 0
1

index 1
2

index 2
3

index 3
4

index 4

index 5

index 6

index 7
len=5
cap=8
Before append (cap=4)
array
0

index 0
1

index 1
2

index 2
3

index 3
len=4
cap=4


第5章 copy 函数底层实现

5.1 copy 源码

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
    }

    // 零大小元素
    if width == 0 {
        return n
    }

    size := uintptr(n) * width
    
    // 竞争检测、内存消毒器等 instrumentation
    if raceenabled {
        callerpc := sys.GetCallerPC()
        pc := abi.FuncPCABIInternal(slicecopy)
        racereadrangepc(fromPtr, size, callerpc, pc)
        racewriterangepc(toPtr, size, callerpc, pc)
    }
    if msanenabled {
        msanread(fromPtr, size)
        msanwrite(toPtr, size)
    }
    if asanenabled {
        asanread(fromPtr, size)
        asanwrite(toPtr, size)
    }

    // 特殊优化:单字节复制
    if size == 1 {
        *(*byte)(toPtr) = *(*byte)(fromPtr)
    } else {
        // 通用内存复制
        memmove(toPtr, fromPtr, size)
    }
    return n
}

长短切片覆盖逻辑详解
#mermaid-svg-sXxrDwz5M9eBxEqO{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-sXxrDwz5M9eBxEqO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sXxrDwz5M9eBxEqO .error-icon{fill:#552222;}#mermaid-svg-sXxrDwz5M9eBxEqO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sXxrDwz5M9eBxEqO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sXxrDwz5M9eBxEqO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sXxrDwz5M9eBxEqO .marker.cross{stroke:#333333;}#mermaid-svg-sXxrDwz5M9eBxEqO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sXxrDwz5M9eBxEqO p{margin:0;}#mermaid-svg-sXxrDwz5M9eBxEqO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster-label text{fill:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster-label span{color:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster-label span p{background-color:transparent;}#mermaid-svg-sXxrDwz5M9eBxEqO .label text,#mermaid-svg-sXxrDwz5M9eBxEqO span{fill:#333;color:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO .node rect,#mermaid-svg-sXxrDwz5M9eBxEqO .node circle,#mermaid-svg-sXxrDwz5M9eBxEqO .node ellipse,#mermaid-svg-sXxrDwz5M9eBxEqO .node polygon,#mermaid-svg-sXxrDwz5M9eBxEqO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sXxrDwz5M9eBxEqO .rough-node .label text,#mermaid-svg-sXxrDwz5M9eBxEqO .node .label text,#mermaid-svg-sXxrDwz5M9eBxEqO .image-shape .label,#mermaid-svg-sXxrDwz5M9eBxEqO .icon-shape .label{text-anchor:middle;}#mermaid-svg-sXxrDwz5M9eBxEqO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sXxrDwz5M9eBxEqO .rough-node .label,#mermaid-svg-sXxrDwz5M9eBxEqO .node .label,#mermaid-svg-sXxrDwz5M9eBxEqO .image-shape .label,#mermaid-svg-sXxrDwz5M9eBxEqO .icon-shape .label{text-align:center;}#mermaid-svg-sXxrDwz5M9eBxEqO .node.clickable{cursor:pointer;}#mermaid-svg-sXxrDwz5M9eBxEqO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sXxrDwz5M9eBxEqO .arrowheadPath{fill:#333333;}#mermaid-svg-sXxrDwz5M9eBxEqO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sXxrDwz5M9eBxEqO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sXxrDwz5M9eBxEqO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sXxrDwz5M9eBxEqO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sXxrDwz5M9eBxEqO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sXxrDwz5M9eBxEqO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster text{fill:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO .cluster span{color:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO 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-sXxrDwz5M9eBxEqO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sXxrDwz5M9eBxEqO rect.text{fill:none;stroke-width:0;}#mermaid-svg-sXxrDwz5M9eBxEqO .icon-shape,#mermaid-svg-sXxrDwz5M9eBxEqO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sXxrDwz5M9eBxEqO .icon-shape p,#mermaid-svg-sXxrDwz5M9eBxEqO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sXxrDwz5M9eBxEqO .icon-shape rect,#mermaid-svg-sXxrDwz5M9eBxEqO .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sXxrDwz5M9eBxEqO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sXxrDwz5M9eBxEqO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sXxrDwz5M9eBxEqO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dst 短
src 短
等长
copy(dst, src)
len(dst) vs len(src)
复制 len(dst) 个元素

src 超出部分丢弃
复制 len(src) 个元素

dst 多余部分不变
完整复制所有元素

5.2 memmove 优化

memmove 是汇编优化实现,支持:

  • 重叠内存区域的安全复制
  • 根据大小选择不同的复制策略(字节、字、向量指令)
  • 针对不同架构的优化(AVX、SSE 等)

为什么 memmove 很快?

  1. 汇编实现,高度优化
  2. 按字节块复制,不是逐个元素
  3. 处理重叠情况(目标和源重叠时)
  4. 利用 SIMD 指令加速现代 CPU

5.3 深拷贝 vs 浅拷贝

go 复制代码
// 浅拷贝:仅拷贝 slice header,共享底层数组
s1 := []int{1, 2, 3}
s2 := s1  // header 拷贝,array 指向同一片内存
s2[0] = 999
fmt.Println(s1)  // [999, 2, 3]

// 深拷贝1:使用 copy
s3 := make([]int, len(s1))
copy(s3, s1)  // 复制元素,不共享底层数组
s3[0] = 888
fmt.Println(s1)  // [999, 2, 3]

// 深拷贝2:append 方式
s4 := append([]int(nil), s1...)

// 深拷贝3:元素是指针时需要递归拷贝
type Data struct{ val int }
d1 := []*Data{{1}, {2}, {3}}
d2 := make([]*Data, len(d1))
for i, v := range d1 {
    // 必须复制指针指向的对象
    tmp := *v
    d2[i] = &tmp
}

第6章 Slice 传参与修改副作用

6.1 Slice 传参内存图

#mermaid-svg-IUwVGTZnul8K4MiI{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-IUwVGTZnul8K4MiI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IUwVGTZnul8K4MiI .error-icon{fill:#552222;}#mermaid-svg-IUwVGTZnul8K4MiI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IUwVGTZnul8K4MiI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IUwVGTZnul8K4MiI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IUwVGTZnul8K4MiI .marker.cross{stroke:#333333;}#mermaid-svg-IUwVGTZnul8K4MiI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IUwVGTZnul8K4MiI p{margin:0;}#mermaid-svg-IUwVGTZnul8K4MiI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IUwVGTZnul8K4MiI .cluster-label text{fill:#333;}#mermaid-svg-IUwVGTZnul8K4MiI .cluster-label span{color:#333;}#mermaid-svg-IUwVGTZnul8K4MiI .cluster-label span p{background-color:transparent;}#mermaid-svg-IUwVGTZnul8K4MiI .label text,#mermaid-svg-IUwVGTZnul8K4MiI span{fill:#333;color:#333;}#mermaid-svg-IUwVGTZnul8K4MiI .node rect,#mermaid-svg-IUwVGTZnul8K4MiI .node circle,#mermaid-svg-IUwVGTZnul8K4MiI .node ellipse,#mermaid-svg-IUwVGTZnul8K4MiI .node polygon,#mermaid-svg-IUwVGTZnul8K4MiI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IUwVGTZnul8K4MiI .rough-node .label text,#mermaid-svg-IUwVGTZnul8K4MiI .node .label text,#mermaid-svg-IUwVGTZnul8K4MiI .image-shape .label,#mermaid-svg-IUwVGTZnul8K4MiI .icon-shape .label{text-anchor:middle;}#mermaid-svg-IUwVGTZnul8K4MiI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IUwVGTZnul8K4MiI .rough-node .label,#mermaid-svg-IUwVGTZnul8K4MiI .node .label,#mermaid-svg-IUwVGTZnul8K4MiI .image-shape .label,#mermaid-svg-IUwVGTZnul8K4MiI .icon-shape .label{text-align:center;}#mermaid-svg-IUwVGTZnul8K4MiI .node.clickable{cursor:pointer;}#mermaid-svg-IUwVGTZnul8K4MiI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IUwVGTZnul8K4MiI .arrowheadPath{fill:#333333;}#mermaid-svg-IUwVGTZnul8K4MiI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IUwVGTZnul8K4MiI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IUwVGTZnul8K4MiI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IUwVGTZnul8K4MiI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IUwVGTZnul8K4MiI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IUwVGTZnul8K4MiI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IUwVGTZnul8K4MiI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IUwVGTZnul8K4MiI .cluster text{fill:#333;}#mermaid-svg-IUwVGTZnul8K4MiI .cluster span{color:#333;}#mermaid-svg-IUwVGTZnul8K4MiI 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-IUwVGTZnul8K4MiI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IUwVGTZnul8K4MiI rect.text{fill:none;stroke-width:0;}#mermaid-svg-IUwVGTZnul8K4MiI .icon-shape,#mermaid-svg-IUwVGTZnul8K4MiI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IUwVGTZnul8K4MiI .icon-shape p,#mermaid-svg-IUwVGTZnul8K4MiI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IUwVGTZnul8K4MiI .icon-shape rect,#mermaid-svg-IUwVGTZnul8K4MiI .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IUwVGTZnul8K4MiI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IUwVGTZnul8K4MiI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IUwVGTZnul8K4MiI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Heap
Function Stack
Caller Stack
Slice Header
array

0x1000
len

5
cap

10
Parameter Copy
array

0x1000
len

5
cap

10
0x1000
elem 0
elem 1
elem 2
elem 3
elem 4

6.2 传参本质

go 复制代码
func modifySlice(s []int) {
    // s 是 slice header 的副本
    // s.array 与原 slice 指向同一数组
    
    s[0] = 999  // ✅ 会修改原数组(通过共享指针)
    
    s = append(s, 5)  // ⚠️ 仅修改副本的 len、cap
    s[1] = 888         // 可能已在新数组上修改,原 slice 不可见
}

func main() {
    original := []int{1, 2, 3}
    modifySlice(original)
    fmt.Println(original)  // [999, 2, 3] - 注意 len 没有变
}

6.3 返回新 slice 的正确方式

go 复制代码
func appendElement(s []int, elem int) []int {
    s = append(s, elem)
    return s  // 返回可能被重新分配的 slice
}

func main() {
    s := []int{1, 2, 3}
    s = appendElement(s, 4)  // 必须重新赋值
    fmt.Println(s)  // [1, 2, 3, 4]
}

6.4 传递指针的场景

go 复制代码
func modifySlicePtr(s *[]int) {
    *s = append(*s, 4)  // 通过指针修改原 slice
    (*s)[0] = 999
}

func main() {
    s := []int{1, 2, 3}
    modifySlicePtr(&s)
    fmt.Println(s)  // [999, 2, 3, 4]
}

核心结论

  1. slice 是值传递: 函数接收的是 slice 结构体的副本
  2. 结构体很小: 只有 24 字节,拷贝开销可忽略
  3. array 指针共享: 函数内修改 slice 元素会影响调用者
  4. append 可能分离: 函数内 append 触发扩容后,函数内的 slice 指向新数组,调用者不知道
  5. 要修改 slice 本身需传指针: func f(s *[]int)

第7章 Slice 内存逃逸与 GC

7.1 逃逸分析

go 复制代码
// 示例1:不逃逸,在栈上分配
func noEscape() int {
    s := make([]int, 10)  // 小切片,不传递到外部
    s[0] = 1
    return s[0]
}

// 示例2:逃逸到堆
func escapeToHeap() []int {
    s := make([]int, 10)  // 返回给调用者,必须在堆上
    return s
}

// 示例3:大切片逃逸
func largeSlice() {
    s := make([]int, 10000)  // 超过栈大小限制,逃逸
    _ = s
}

// 示例4:闭包捕获导致逃逸
func closureEscape() func() int {
    s := make([]int, 10)  // 被闭包捕获,逃逸
    return func() int {
        return len(s)
    }
}

7.2 GC 对 Slice 的处理

#mermaid-svg-Sbr8PUkcZHHFRjlM{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-Sbr8PUkcZHHFRjlM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Sbr8PUkcZHHFRjlM .error-icon{fill:#552222;}#mermaid-svg-Sbr8PUkcZHHFRjlM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Sbr8PUkcZHHFRjlM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .marker.cross{stroke:#333333;}#mermaid-svg-Sbr8PUkcZHHFRjlM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Sbr8PUkcZHHFRjlM p{margin:0;}#mermaid-svg-Sbr8PUkcZHHFRjlM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster-label text{fill:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster-label span{color:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster-label span p{background-color:transparent;}#mermaid-svg-Sbr8PUkcZHHFRjlM .label text,#mermaid-svg-Sbr8PUkcZHHFRjlM span{fill:#333;color:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .node rect,#mermaid-svg-Sbr8PUkcZHHFRjlM .node circle,#mermaid-svg-Sbr8PUkcZHHFRjlM .node ellipse,#mermaid-svg-Sbr8PUkcZHHFRjlM .node polygon,#mermaid-svg-Sbr8PUkcZHHFRjlM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .rough-node .label text,#mermaid-svg-Sbr8PUkcZHHFRjlM .node .label text,#mermaid-svg-Sbr8PUkcZHHFRjlM .image-shape .label,#mermaid-svg-Sbr8PUkcZHHFRjlM .icon-shape .label{text-anchor:middle;}#mermaid-svg-Sbr8PUkcZHHFRjlM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .rough-node .label,#mermaid-svg-Sbr8PUkcZHHFRjlM .node .label,#mermaid-svg-Sbr8PUkcZHHFRjlM .image-shape .label,#mermaid-svg-Sbr8PUkcZHHFRjlM .icon-shape .label{text-align:center;}#mermaid-svg-Sbr8PUkcZHHFRjlM .node.clickable{cursor:pointer;}#mermaid-svg-Sbr8PUkcZHHFRjlM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .arrowheadPath{fill:#333333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Sbr8PUkcZHHFRjlM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Sbr8PUkcZHHFRjlM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Sbr8PUkcZHHFRjlM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster text{fill:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM .cluster span{color:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM 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-Sbr8PUkcZHHFRjlM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Sbr8PUkcZHHFRjlM rect.text{fill:none;stroke-width:0;}#mermaid-svg-Sbr8PUkcZHHFRjlM .icon-shape,#mermaid-svg-Sbr8PUkcZHHFRjlM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Sbr8PUkcZHHFRjlM .icon-shape p,#mermaid-svg-Sbr8PUkcZHHFRjlM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Sbr8PUkcZHHFRjlM .icon-shape rect,#mermaid-svg-Sbr8PUkcZHHFRjlM .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Sbr8PUkcZHHFRjlM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Sbr8PUkcZHHFRjlM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Sbr8PUkcZHHFRjlM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} array pointer
if elem has pointers
Slice Header
Backing Array
扫描元素
注意
只有 slice.array 指向的

部分会被视为存活
len 之前的元素: 存活
len 到 cap 之间的元素: 不扫描

7.3 内存泄漏陷阱

go 复制代码
// 陷阱1:子切片保留大数组引用
func leak1() {
    data := make([]byte, 1<<20)  // 1MB
    small := data[:10]            // 仅用 10 字节
    // 即使 data 不再使用,整个 1MB 数组也不会被回收
    // 因为 small 的 array 还指向它
    _ = small
}

// 修复:显式复制
func noLeak1() {
    data := make([]byte, 1<<20)
    small := make([]byte, 10)
    copy(small, data)
    // data 现在可以被回收了
    _ = small
}

// 陷阱2:切片中的指针阻止元素回收
func leak2() {
    type Node struct{ val int }
    s := make([]*Node, 100)
    for i := range s {
        s[i] = &Node{i}
    }
    // 即使只保留前 5 个
    s = s[:5]
    // 后面 95 个 Node 的内存不会被回收!
    // 因为 s 的 cap=100,数组还在,指针还存在
}

// 修复:显式置 nil
func noLeak2() {
    s := make([]*Node, 100)
    // ... 填充 ...
    
    // 截断前先清理
    for i := 5; i < len(s); i++ {
        s[i] = nil
    }
    s = s[:5]
}

第8章 项目代码实战解析

8.1 strings.Builder 中的 Slice 应用

源码位置 : src/strings/builder.go

go 复制代码
type Builder struct {
    addr *Builder  // 检测复制
    buf  []byte    // 底层字节切片
}

func (b *Builder) copyCheck() {
    if b.addr == nil {
        // 避免 b 逃逸到堆的技巧
        b.addr = (*Builder)(abi.NoEscape(unsafe.Pointer(b)))
    } else if b.addr != b {
        panic("strings: illegal use of non-zero Builder copied by value")
    }
}

// Write 使用 append,可能触发扩容
func (b *Builder) Write(p []byte) (int, error) {
    b.copyCheck()
    b.buf = append(b.buf, p...)  // 关键:append 可能调用 growslice
    return len(p), nil
}

// Grow 预分配
func (b *Builder) Grow(n int) {
    b.copyCheck()
    if n < 0 {
        panic("strings.Builder.Grow: negative count")
    }
    if cap(b.buf)-len(b.buf) < n {
        b.grow(n)  // 显式扩容
    }
}

func (b *Builder) grow(n int) {
    // 策略:2 倍扩容 + n
    buf := bytealg.MakeNoZero(2*cap(b.buf) + n)[:len(b.buf)]
    copy(buf, b.buf)
    b.buf = buf
}

// String 零拷贝转换
func (b *Builder) String() string {
    // 直接使用底层数组,避免内存拷贝
    return unsafe.String(unsafe.SliceData(b.buf), len(b.buf))
}

底层原理解析:

  • append(b.buf, p...)b.buf 容量不足时调用 runtime.growslice
  • Grow 方法实现了自定义的扩容策略(2x + n)
  • String 方法使用 unsafe 进行零拷贝转换,避免内存分配

8.2 makeslice 源码分析


切片创建完整流程
#mermaid-svg-U3dbxHMx5PWocWEe{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-U3dbxHMx5PWocWEe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U3dbxHMx5PWocWEe .error-icon{fill:#552222;}#mermaid-svg-U3dbxHMx5PWocWEe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U3dbxHMx5PWocWEe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U3dbxHMx5PWocWEe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U3dbxHMx5PWocWEe .marker.cross{stroke:#333333;}#mermaid-svg-U3dbxHMx5PWocWEe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U3dbxHMx5PWocWEe p{margin:0;}#mermaid-svg-U3dbxHMx5PWocWEe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-U3dbxHMx5PWocWEe .cluster-label text{fill:#333;}#mermaid-svg-U3dbxHMx5PWocWEe .cluster-label span{color:#333;}#mermaid-svg-U3dbxHMx5PWocWEe .cluster-label span p{background-color:transparent;}#mermaid-svg-U3dbxHMx5PWocWEe .label text,#mermaid-svg-U3dbxHMx5PWocWEe span{fill:#333;color:#333;}#mermaid-svg-U3dbxHMx5PWocWEe .node rect,#mermaid-svg-U3dbxHMx5PWocWEe .node circle,#mermaid-svg-U3dbxHMx5PWocWEe .node ellipse,#mermaid-svg-U3dbxHMx5PWocWEe .node polygon,#mermaid-svg-U3dbxHMx5PWocWEe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U3dbxHMx5PWocWEe .rough-node .label text,#mermaid-svg-U3dbxHMx5PWocWEe .node .label text,#mermaid-svg-U3dbxHMx5PWocWEe .image-shape .label,#mermaid-svg-U3dbxHMx5PWocWEe .icon-shape .label{text-anchor:middle;}#mermaid-svg-U3dbxHMx5PWocWEe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U3dbxHMx5PWocWEe .rough-node .label,#mermaid-svg-U3dbxHMx5PWocWEe .node .label,#mermaid-svg-U3dbxHMx5PWocWEe .image-shape .label,#mermaid-svg-U3dbxHMx5PWocWEe .icon-shape .label{text-align:center;}#mermaid-svg-U3dbxHMx5PWocWEe .node.clickable{cursor:pointer;}#mermaid-svg-U3dbxHMx5PWocWEe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U3dbxHMx5PWocWEe .arrowheadPath{fill:#333333;}#mermaid-svg-U3dbxHMx5PWocWEe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U3dbxHMx5PWocWEe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U3dbxHMx5PWocWEe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3dbxHMx5PWocWEe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U3dbxHMx5PWocWEe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3dbxHMx5PWocWEe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U3dbxHMx5PWocWEe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U3dbxHMx5PWocWEe .cluster text{fill:#333;}#mermaid-svg-U3dbxHMx5PWocWEe .cluster span{color:#333;}#mermaid-svg-U3dbxHMx5PWocWEe 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-U3dbxHMx5PWocWEe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U3dbxHMx5PWocWEe rect.text{fill:none;stroke-width:0;}#mermaid-svg-U3dbxHMx5PWocWEe .icon-shape,#mermaid-svg-U3dbxHMx5PWocWEe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3dbxHMx5PWocWEe .icon-shape p,#mermaid-svg-U3dbxHMx5PWocWEe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U3dbxHMx5PWocWEe .icon-shape rect,#mermaid-svg-U3dbxHMx5PWocWEe .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3dbxHMx5PWocWEe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U3dbxHMx5PWocWEe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U3dbxHMx5PWocWEe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是







make(\[\]T, len, cap)
检查 len < 0
panic len out of range
检查 len > cap
panic cap out of range
计算 mem = cap * sizeof(T)
检查乘法溢出
mem > maxAlloc
mallocgc(mem, T, true)
内存自动清零
返回 slice{array, len, cap}


源码位置 : src/runtime/slice.go

go 复制代码
func makeslice(et *_type, len, cap int) unsafe.Pointer {
    // 计算需要的内存大小,检查溢出
    mem, overflow := math.MulUintptr(et.Size_, uintptr(cap))
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        // 错误处理优化:优先报告 len 错误而不是 cap
        mem, overflow := math.MulUintptr(et.Size_, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 {
            panicmakeslicelen()
        }
        panicmakeslicecap()
    }

    // 分配内存,自动零值化
    return mallocgc(mem, et, true)
}

func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
    var tomem, copymem uintptr
    if uintptr(tolen) > uintptr(fromlen) {
        var overflow bool
        tomem, overflow = math.MulUintptr(et.Size_, uintptr(tolen))
        if overflow || tomem > maxAlloc || tolen < 0 {
            panicmakeslicelen()
        }
        copymem = et.Size_ * uintptr(fromlen)
    } else {
        tomem = et.Size_ * uintptr(tolen)
        copymem = tomem
    }

    var to unsafe.Pointer
    if !et.Pointers() {
        // 不含指针的类型:分配后不清零
        to = mallocgc(tomem, nil, false)
        if copymem < tomem {
            memclrNoHeapPointers(add(to, copymem), tomem-copymem)
        }
    } else {
        // 含指针的类型:必须清零,避免 GC 扫描未初始化内存
        to = mallocgc(tomem, et, true)
        if copymem > 0 && writeBarrier.enabled {
            bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem, et)
        }
    }

    // 复制数据
    memmove(to, from, copymem)

    return to
}

底层原理解析:

  • math.MulUintptr 用于安全的乘法溢出检查
  • 根据类型是否包含指针选择不同的分配策略
  • 写屏障(writeBarrier.enabled)用于并发 GC 支持

maxAlloc 是什么?

  • 64位系统:通常是 2^47 字节(~140TB)
  • 32位系统:通常是 2^32 字节(4GB)
  • 防止分配过大的内存导致系统问题

8.3 slicecopy 在标准库中的应用

源码位置 : src/strings/strings.go

go 复制代码
func explode(s string, n int) []string {
    l := utf8.RuneCountInString(s)
    if n < 0 || n > l {
        n = l
    }
    a := make([]string, n)  // 调用 makeslice
    for i := 0; i < n-1; i++ {
        _, size := utf8.DecodeRuneInString(s)
        a[i] = s[:size]      // 字符串切片(类似 slice 截取)
        s = s[size:]
    }
    if n > 0 {
        a[n-1] = s
    }
    return a
}

底层原理解析:

  • make([]string, n) 调用 runtime.makeslice
  • 字符串截取 s[:size] 不分配新内存,只是创建新的 string header
  • 字符串和 slice 共享相同的内存布局设计

8.4 扩容实战示例

go 复制代码
// 示例:追踪扩容过程
func traceGrow() {
    s := make([]int, 0)
    prevCap := 0
    
    for i := 0; i < 2000; i++ {
        s = append(s, i)
        if cap(s) != prevCap {
            fmt.Printf("i=%4d, len=%4d, cap=%4d\n", i, len(s), cap(s))
            prevCap = cap(s)
        }
    }
}

// 输出示例:
// i=   0, len=   1, cap=   1  (0 -> 1)
// i=   1, len=   2, cap=   2  (1 -> 2)
// i=   2, len=   3, cap=   4  (2 -> 4)
// i=   4, len=   5, cap=   8  (4 -> 8)
// i=   8, len=   9, cap=  16  (8 -> 16)
// ...
// i= 256, len= 257, cap= 512  (256 -> 512,阈值后增长变缓)
// i= 512, len= 513, cap= 768  (512 -> 768,增长 256 = (512+768)/4)
// ...

第9章 常见问题与面试题

9.1 常见面试题

Q1: 切片和数组的区别?

特性 数组 切片
类型 [N]T,长度是类型一部分 []T,长度不参与类型
长度 固定,不可变 动态,可增长
内存 直接存储元素 存储指针+len+cap
传参 值拷贝整个数组 仅拷贝 header(24 字节)

Q2: nil 切片和空切片的区别?

go 复制代码
var s1 []int      // nil 切片,array = nil
s2 := []int{}     // 空切片,array = &zerobase
s3 := make([]int, 0)  // 空切片,array = &zerobase

fmt.Println(s1 == nil)  // true
fmt.Println(s2 == nil)  // false
fmt.Println(len(s1) == len(s2))  // true, 都为 0

Q3: append 什么时候会扩容?

go 复制代码
s := make([]int, 3, 5)
s = append(s, 1)  // len=4 <= cap=5,不扩容
s = append(s, 2)  // len=5 <= cap=5,不扩容
s = append(s, 3)  // len=6 > cap=5,触发扩容!

Q4: 切片作为参数传递,能修改原切片吗?

go 复制代码
func modify(s []int) {
    s[0] = 999  // ✅ 能修改底层数组
    s = append(s, 4)  // ⚠️ 仅修改副本的 len,不影响原 slice
}

func modifyPtr(s *[]int) {
    *s = append(*s, 4)  // ✅ 能修改原 slice
}

Q5: 切片截取会共享内存吗?

go 复制代码
original := []int{1, 2, 3, 4, 5}
s := original[1:3]  // [2, 3],共享底层数组
s[0] = 999
fmt.Println(original)  // [1, 999, 3, 4, 5] - 被修改了!

Q6: 如何避免内存泄漏?

go 复制代码
// 问题:子切片持有大数组引用
data := make([]byte, 1<<20)  // 1MB
small := data[:10]  // 整个 1MB 都不会被回收

// 解决:显式复制
small := make([]byte, 10)
copy(small, data)  // 现在 data 可以被回收

// 问题:指针切片截断后元素不回收
s := make([]*int, 100)
// ... 填充 ...
for i := 10; i < len(s); i++ {
    s[i] = nil  // 先置 nil,再截断
}
s = s[:10]

Q7: copy 的返回值是什么?

go 复制代码
a := []int{1, 2, 3}
b := []int{4, 5}
n := copy(a, b)  // n = 2,取较小长度
fmt.Println(a)   // [4, 5, 3]

Q8: 如何高效预分配?

go 复制代码
// 低效:多次扩容
s := make([]int, 0)
for i := 0; i < 1000; i++ {
    s = append(s, i)  // 可能触发 10 次扩容
}

// 高效:预分配
s := make([]int, 0, 1000)  // 预分配 cap=1000
for i := 0; i < 1000; i++ {
    s = append(s, i)  // 不会扩容
}

Q9: for range 切片时的注意事项?

go 复制代码
s := []int{1, 2, 3}
for i, v := range s {
    // v 是元素的副本,修改 v 不影响原切片
    v *= 2
    
    // 可以通过索引修改
    s[i] *= 2
}

Q10: Slice Header 的大小是多少?

复制代码
在 64 位系统上:
- unsafe.Pointer: 8 字节
- int: 8 字节
- int: 8 字节
总计: 24 字节

在 32 位系统上:
- unsafe.Pointer: 4 字节
- int: 4 字节
- int: 4 字节
总计: 12 字节

9.2 最佳实践

  1. 预分配容量 :如果知道最终大小,用 make([]T, 0, cap) 预分配
  2. 避免不必要的 appends = append([]T(nil), s...) 这种写法没意义
  3. 注意共享底层数组:截取操作可能导致意外的内存共享
  4. 及时清理指针:包含指针的切片截断前先置 nil
  5. 子切片考虑复制:如果只需要小部分,考虑 copy 而不是截取
  6. 不要保存 len 之前的索引:扩容后索引会失效

附录:完整源码链接

  • src/runtime/slice.go - Slice 核心实现
  • src/runtime/malloc.go - 内存分配
  • src/runtime/string.go - 字符串相关(与 slice 有紧密联系)
  • src/strings/builder.go - 标准库 Builder 实践
  • src/runtime/type.go - 类型系统

总结

Slice 是 Go 中最常用的数据结构之一,理解其底层原理对于写出高效、正确的代码至关重要:

  1. 结构简单但精巧:只有三个字段,但功能强大
  2. 共享底层数组:截取、传参时注意共享问题
  3. 扩容有策略:小切片 2 倍,大切片渐进式增长
  4. 内存要管理:注意内存逃逸、泄漏问题
  5. 零值可用:nil 切片可以直接 append
相关推荐
Hesionberger1 小时前
快速求解完全平方数的最少数量
开发语言·数据结构·python·算法·leetcode·c#
程序员爱钓鱼1 小时前
Rust 变量与不可变性:为什么默认不能修改变量?
前端·后端·rust
GoGeekBaird10 小时前
我开源了 BeeWeave,给 AI Agent 搭一个越用越懂你的知识创作台
后端·github
西门吹-禅12 小时前
java springboot N+1问题
java·开发语言·spring boot
skywalk816312 小时前
设计并实现段言的 C FFI 绑定机制 @Trae
c语言·开发语言·python·编程
犀利豆13 小时前
AI in Harness(四)
人工智能·后端
Hui Baby13 小时前
Spring Security
java·后端·spring
IT笔记13 小时前
【Rust】Rust Match 模式匹配详解
java·开发语言·rust
2zcode14 小时前
免费开源项目文档:基于MATLAB卷积神经网络的口罩佩戴检测系统
开发语言·matlab·cnn