序
问题回顾
gdb
- 请查看下面一段代码,会输出什么,为什么?
- A: 5 8 B: 8 8 C: 5 5 D: 5 6
package main
import (
"fmt"
)
func main() {
s := []int{1, 2}
s = append(s, 3, 4, 5)
fmt.Println(len(s), cap(s))
}
s2s30x587450
slice 扩容算法是否遗漏了什么?
首先我们回顾一下上一篇文章当中讲到的 slice 扩容的 算法:
- 如果需要的最小容量比两倍原有容量大,那么就取需要的容量
- 如果原有 slice 长度小于 1024 那么每次就扩容为原来的两倍
- 如果原 slice 大于等于 1024 那么每次扩容就扩为原来的 1.25 倍
按照这个逻辑进行套用:
slen=2s = append(s, 3, 4, 5)5 > 2*2C: 5 5D: 5 6
接下来我们就使用 gdb 调试一下看看结果
使用 GDB 调试 Golang 代码
VS CodeGoland
安装
brew install gdb # for mac
如果是 linux 使用自带的包管理工具进行安装即可
注意安装完成之后需要在 home 目录上添加相关配置
vim ~/.gdbinit
# 输入下面
add-auto-load-safe-path $GOROOT/src/runtime/runtime-gdb.py # 这里将 $GOROOT 替换为你的 GO 安装目录
如果你是 mac 首次安装 gdb 需要给 gdb 签名,可以参考: https://gist.github.com/hlissner/898b7dfc0a3b63824a70e15cd0180154
编译代码
-gcflags=all="-N -l"-ldflags='-compressdwarf=false'No symbol table is loaded. Use the "file" command.-ldflags='-compressdwarf=false'
go build -o bin/03_q1_slice_cap -gcflags=all="-N -l" -ldflags='-compressdwarf=false' 02_array/03_q1_slice_cap/main.go
进入 GDB 调试窗口
-tui
gdb -tui ./bin/03_q1_slice_cap
Loading Go Runtime support.
常用调试命令
b 文件名:行数info brcsnuntiluntil:行号info localsinfo argsinfo goroutineshelpq
调试
知道如何操作之后,我们开始干活
b main.go:11rslices
ninfo localsnewcapn
newcapcapmem=48PtrSize=8newcap
知道问题出现在哪里之后我们可以再来看一下源代码,有一个内存对齐的函数
// Returns size of the memory block that mallocgc will allocate if you ask for the size.
func roundupsize(size uintptr) uintptr {
if size < _MaxSmallSize {
if size <= smallSizeMax-8 {
return uintptr(class_to_size[size_to_class8[divRoundUp(size, smallSizeDiv)]])
} else {
return uintptr(class_to_size[size_to_class128[divRoundUp(size-smallSizeMax, largeSizeDiv)]])
}
}
if size+_PageSize < size {
return size
}
return alignUp(size, _PageSize)
}
const (
_MaxSmallSize = 32768
smallSizeDiv = 8
smallSizeMax = 1024
largeSizeDiv = 128
_NumSizeClasses = 67
_PageShift = 13
)
var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, ...}
var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, ...}
capmem = roundupsize(uintptr(newcap) * sys.PtrSize)5*8=4048
总结
slice 扩容算法
- 如果需要的最小容量比两倍原有容量大,那么就取需要的容量
- 如果原有 slice 长度小于 1024 那么每次就扩容为原来的两倍
- 如果原 slice 大于等于 1024 那么每次扩容就扩为原来的 1.25 倍
- 除此之外扩容容量计算完成之后,还会进行一次内存对齐操作
搜索 slice 扩容策略很多都会说第 2、3 点但是没有说 1, 4 点就会造成一些困惑
GDB 调试
- mac 下安装使用 gdb 比较麻烦,建议使用 linux
- 一般情况下我们还是建议使用 dlv 进行调试,如果有 runtime 库的调试需求可以使用 gdb
- 学习了 gdb 的安装以及基本使用方式,你之前有类似的经历么?一般会在什么情况下使用 gdb 进行调试
s2s30x587450
makeslicemallocgc
if size == 0 {
return unsafe.Pointer(&zerobase)
}