注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本文章主要是为了回答上一篇文章的问题,并且介绍一下在这个过程中以及后续都会使用到的调试工具 gdb
1 | |
s2 , s3 打印的数组指针都是 0x587450 ?首先我们回顾一下上一篇文章当中讲到的 slice 扩容的 算法:
按照这个逻辑进行套用:
s 初始化时 len=2s = append(s, 3, 4, 5) 此时需要的最小容量为 5 > 2*2C: 5 5D: 5 6 为什么呢?这个逻辑和我们之前了解到的不太一样啊接下来我们就使用 gdb 调试一下看看结果
调试 go 程序我们常用的调试工具其实是 dlv 这个工具非常好用,并且可以很好的和 VS Code Goland 等 IDE 进行结合,但是它无法调试 runtime 的代码,这个时候就要使用上 gdb 了,如果大家不需要调试 runtime 的代码的话还是建议使用 dlv
1 | |
如果是 linux 使用自带的包管理工具进行安装即可
注意安装完成之后需要在 home 目录上添加相关配置
1 | |
如果你是 mac 首次安装 gdb 需要给 gdb 签名,可以参考: https://gist.github.com/hlissner/898b7dfc0a3b63824a70e15cd0180154
-gcflags=all="-N -l" 禁用内联优化方便后面调试-ldflags='-compressdwarf=false' 在 gdb 调试的时候可能会提示 No symbol table is loaded. Use the "file" command.-ldflags='-compressdwarf=false' 这个标志可以禁用压缩方便调试1 | |
-tui 表示同时显示代码窗口1 | |
如下图所示,回车几次之后出现 Loading Go Runtime support. 就说明正常了
b 文件名:行数 打断点info b 当前的断点情况r 运行程序知道断点处c 继续执行到下一个断点s 单步执行,如果有调用函数则进入函数,注意和 n 的区别n 单步执行,如果有调用的函数不会进入函数内部until 退出循环until:行号 执行到指定行info locals 当前堆栈的所有变量info args 打印参数info goroutines 查看所有的 goroutine 及其 IDhelp 帮助q 退出知道如何操作之后,我们开始干活
b main.go:11rslice 的扩容函数中 s
n 一直单步执行到扩容算法结束,使用 info locals 打印变量。我们可以发现这个时候计算出的 newcap 和我们最初预计的一样是 5,那哪里出了问题呢?我们继续使用 n 接着看
newcap 被重新赋值了,并且这个时候 capmem=48 PtrSize=8 所以最后的出来了 newcap 等于 6
知道问题出现在哪里之后我们可以再来看一下源代码,有一个内存对齐的函数
1 | |
可以发现我们之前在计算 capmem 的时候传入的 capmem = roundupsize(uintptr(newcap) * sys.PtrSize) 值是 5*8=40 最后对齐出的结果就是 48 了
搜索 slice 扩容策略很多都会说第 2、3 点但是没有说 1, 4 点就会造成一些困惑
s2 , s3 打印的数组指针都是 0x587450 ?在讲解 makeslice 的时候我们有说到最后一步会调用 mallocgc 分配内存,看这个函数的源码我们就能发现,有一步判断,如果容量为 0 会返回一个固定的地址
1 | |
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本文章主要是为了回答上一篇文章的问题,并且介绍一下在这个过程中以及后续都会使用到的调试工具 gdb
1 | |
s2 , s3 打印的数组指针都是 0x587450 ?首先我们回顾一下上一篇文章当中讲到的 slice 扩容的 算法:
按照这个逻辑进行套用:
s 初始化时 len=2s = append(s, 3, 4, 5) 此时需要的最小容量为 5 > 2*2C: 5 5D: 5 6 为什么呢?这个逻辑和我们之前了解到的不太一样啊接下来我们就使用 gdb 调试一下看看结果
调试 go 程序我们常用的调试工具其实是 dlv 这个工具非常好用,并且可以很好的和 VS Code Goland 等 IDE 进行结合,但是它无法调试 runtime 的代码,这个时候就要使用上 gdb 了,如果大家不需要调试 runtime 的代码的话还是建议使用 dlv
1 | |
如果是 linux 使用自带的包管理工具进行安装即可
注意安装完成之后需要在 home 目录上添加相关配置
1 | |
如果你是 mac 首次安装 gdb 需要给 gdb 签名,可以参考: https://gist.github.com/hlissner/898b7dfc0a3b63824a70e15cd0180154
-gcflags=all="-N -l" 禁用内联优化方便后面调试-ldflags='-compressdwarf=false' 在 gdb 调试的时候可能会提示 No symbol table is loaded. Use the "file" command.-ldflags='-compressdwarf=false' 这个标志可以禁用压缩方便调试1 | |
-tui 表示同时显示代码窗口1 | |
如下图所示,回车几次之后出现 Loading Go Runtime support. 就说明正常了
b 文件名:行数 打断点info b 当前的断点情况r 运行程序知道断点处c 继续执行到下一个断点s 单步执行,如果有调用函数则进入函数,注意和 n 的区别n 单步执行,如果有调用的函数不会进入函数内部until 退出循环until:行号 执行到指定行info locals 当前堆栈的所有变量info args 打印参数info goroutines 查看所有的 goroutine 及其 IDhelp 帮助q 退出知道如何操作之后,我们开始干活
b main.go:11rslice 的扩容函数中 s
n 一直单步执行到扩容算法结束,使用 info locals 打印变量。我们可以发现这个时候计算出的 newcap 和我们最初预计的一样是 5,那哪里出了问题呢?我们继续使用 n 接着看
newcap 被重新赋值了,并且这个时候 capmem=48 PtrSize=8 所以最后的出来了 newcap 等于 6
知道问题出现在哪里之后我们可以再来看一下源代码,有一个内存对齐的函数
1 | |
可以发现我们之前在计算 capmem 的时候传入的 capmem = roundupsize(uintptr(newcap) * sys.PtrSize) 值是 5*8=40 最后对齐出的结果就是 48 了
搜索 slice 扩容策略很多都会说第 2、3 点但是没有说 1, 4 点就会造成一些困惑
s2 , s3 打印的数组指针都是 0x587450 ?在讲解 makeslice 的时候我们有说到最后一步会调用 mallocgc 分配内存,看这个函数的源码我们就能发现,有一步判断,如果容量为 0 会返回一个固定的地址
1 | |