知其然,知其所以然
Go 内部原理 2026-10-10

go test 缓存:什么情况下结果会被复用

在 Go 1.27.1 里验证 go test 缓存:没有包参数的 go test 根本不缓存;go test . 第 2 次直接 (cached),-count=1 强制重跑;源码任何改动、-race 会换测试二进制;-short、-v、-timeout 有各自的缓存条目,-count=2 不缓存;只有读过的环境变量变化才失效;读模块内的文件会被跟踪,模块外的文件改了结果仍是旧的;依赖时间的测试结果会被冻住。

Go go test 测试缓存 count=1 go clean -testcache 真实验证 动画演示
Go 内部原理 2026-10-10

覆盖率:go test -cover 是怎么插桩的

在 Go 1.27.1 里实测覆盖率:17 个语句里 8 个被覆盖(47.1%);覆盖率文件按“块”记录起止行列、语句数和计数;set/count/atomic 三种模式的计数含义;紧循环里插桩的开销 set 2.33 倍、count 6.34 倍、atomic 26.41 倍;go build -cover 配合 GOCOVERDIR 让普通程序也能收集覆盖率。

Go 覆盖率 go test -cover covermode 插桩 真实验证 动画演示
Go 内部原理 2026-10-10

go vet:它在检查哪些常见错误

在 Go 1.27.1 里实测 go vet:一个故意写了十几类问题的文件被报告了 16 条(来自 13 个检查器)(printf、copylocks、lostcancel、shift、stringintconv、httpresponse、waitgroup、unreachable 等);vet 共有 35 个检查器;go test 自带的 vet 只跑 11 个高置信度的检查,所以 copylocks 问题能通过 go test 却会被 go vet 拦下。

Go go vet 静态检查 printf copylocks lostcancel 真实验证 动画演示
Go 内部原理 2026-10-10

text/template:管道、变量与 FuncMap 的求值

在 Go 1.27.1 里实测 text/template 的求值规则:管道把结果作为后一个命令的最后一个参数(3 | printf "%d-%d" 1 得到 1-3);:= 与 = 的作用域差别;map 缺 key 默认输出 <no value>,missingkey=error 才报错;空字符串、0、nil、空 slice 为假,or 会短路;FuncMap 必须在 Parse 之前注册,函数返回 error 会中断执行;已解析模板 Execute 612 ns,每次都 Parse 要 3474 ns。

Go text/template 模板 管道 FuncMap 真实验证 动画演示
Go 内部原理 2026-10-10

html/template:上下文感知的转义

在 Go 1.27.1 里实测 html/template 的上下文感知转义:同一个值放在 HTML 文本、属性、URL、JavaScript、CSS、注释里被转义成不同形式(危险协议变成 #ZgotmplZ,JS 值编码成字符串或 JSON,< 变成 \u003c);template.HTML / template.URL 会绕过转义;{{if}} 分支上下文不一致时 Execute 直接报错;Execute 之后不能再 Parse。

Go html/template 转义 XSS 上下文感知 真实验证 动画演示
Go 内部原理 2026-10-10

encoding/gob:类型信息只发一次的二进制编码

在 Go 1.27.1 里实测 encoding/gob:同一个 Encoder 连续编码,第一条消息 51 字节(含类型定义),之后每条只有 11 字节,每次新建 Encoder 则每条都是 51 字节;字段按名字匹配、多出的字段被忽略、类型变了才报错;接口字段必须 Register;复用 Encoder/Decoder 时编码 347 ns、解码 619 ns/条,比 JSON 快,每次新建则慢 3–9 倍。

Go encoding/gob 序列化 类型信息 Encoder 真实验证 动画演示
Go 内部原理 2026-10-10

hash/maphash 与 map 的哈希:每个进程不同的种子

在 Go 1.27.1 里实测 hash/maphash:同一个字符串在三个进程里的哈希值各不相同(种子随进程随机),同一进程内稳定,分段写入与一次性计算相同,Comparable 可以哈希结构体;16 万个键分到 16 个桶几乎均匀;1 KiB 输入上 maphash 77 ns,FNV-1a 1176 ns;内置 map 的遍历起点也因 map 而异。

Go hash/maphash 哈希 随机种子 哈希表 真实验证 动画演示
Go 内部原理 2026-09-23

testing:go test 到底在跑什么,t.Fatal 为什么不能在子 goroutine 里调用

真实用 go test -work 扒出编译期生成的 _testmain.go,证明 go test 就是现扫描 TestXxx/BenchmarkXxx/ExampleXxx 拼出一个真正的 main() 再编译运行;真实验证 t.Fatal() 调用的是 runtime.Goexit(),从子 goroutine 里调用完全不会终止主测试;真实跑出 t.Parallel() 的两阶段暂停/恢复顺序;真实验证 t.Cleanup() 严格按注册的反顺序执行,且在 t.Fatal() 之后依然会跑。

testing 标准库 动画演示
Go 内部原理 2026-09-13

转账系统的死锁:两个 goroutine 反向转账,为什么会真的锁死,固定加锁顺序又是怎么解决的

真实用 Go 复现转账系统的经典死锁:两个 goroutine 反向转账(A→B 和 B→A)真实触发 Go 运行时 fatal error: all goroutines are asleep - deadlock!,附真实的 goroutine 栈;按账户 ID 固定加锁顺序后,2 万轮约 4 万并发 goroutine 反向转账压力测试真实验证零死锁且总额守恒。

死锁 并发 动画演示
Go 内部原理 2026-08-26

encoding/json:struct tag 是怎么被解析、判优先级、缓存下来的

本机真实 Go 1.26.6 程序验证嵌入结构体同名字段的三种冲突消解规则(深度优先、tag 优先、模糊则丢弃),用 -race 验证递归类型的并发安全构建,并实测字段元数据缓存带来的 50 倍加速,对照本机标准库真实源码。

encoding/json 反射 动画演示
Go 内部原理 2026-08-25

sync:Mutex 怎么从"不公平"切换到"公平",WaitGroup 和 Once 又是怎么实现的

本机真实 Go 1.26.6 程序,用 unsafe.Pointer 直接观测 sync.Mutex 内部状态字从正常模式翻转到饥饿模式再翻转回来的完整过程,并验证 WaitGroup 的并发耗时与 Once 的阻塞语义,对照本机标准库真实源码。

sync 并发 动画演示
Go 内部原理 2026-08-24

context:取消信号是怎么在 goroutine 树里传播的

本机真实 Go 1.26.6 程序验证 context 取消传播只往下走不往上走,超时靠 time.AfterFunc 触发,以及嵌套超时的优化,对照本机标准库真实源码。

context 并发 动画演示
京ICP备2026067502号-1京公网安备11010502063487号