实测 git 配置的层级与优先级:system → global → local 后读的覆盖先读的;worktree 层(extensions.worktreeConfig)在 local 之后;-c 与 GIT_CONFIG_COUNT 环境变量更高;include 按位置插入,includeIf gitdir: 可按路径引入不同身份;多值键(如 remote.origin.fetch)叠加,--get 取最后一个,写操作默认只改 local;XDG 与 ~/.gitconfig 同时存在时都读。
实测子命令的查找:git foo 找可执行文件 git-foo(--exec-path 和 PATH);拼错命令的提示与 help.autocorrect=immediate;别名展开,! 开头的别名在仓库根目录用 shell 运行,GIT_PREFIX 保存原目录;别名不能覆盖内置命令,且与外部子命令同名时外部命令胜出(顺序:内置 → 外部 → 别名);别名循环被检测;git help -a 列出 External commands 与 Command aliases。
用 Go 1.27.1 的 -gcflags=-m 与 -m -m 看编译器的逃逸判断,用 testing.AllocsPerRun 数真实分配次数:常量转接口虽然说 escapes to heap 却是 0 次分配,变长 make 在 32 字节以内留在栈上,二分法量出显式局部变量 128 KiB 与隐式分配 64 KiB 的栈上限,同一个函数内联与否结论相反,以及逃到堆上 500 万次比留在栈上慢约 3.3 倍。
用 unsafe 把 Go 1.27.1 的接口值按 {typ, data} 与 {itab, data} 读出来:接口值 16 字节,指针类型直接放进 data 字、值类型是一份拷贝,itab 里是接口类型、动态类型与按方法名排序的方法地址,同一个动态类型装进两种接口得到两个 itab;编译期已知的转换引用 go:itab 符号、断言走 runtime.typeAssert;接口调用在类型固定时与直接调用一样快(约 2 ns),4 种类型随机时约 7.2 ns。
用 Go 1.27.1 复现“返回 nil 指针的 error 不等于 nil”:有错误类型的 nil 指针装进 error 后,接口的类型字非空、数据字为空,与 nil 比较是 false;go vet 没有警告;调用方法会不会 panic 取决于方法是否解引用接收者;nil 的 map、slice、func 装进 any 同样不等于 nil,用 reflect 判断 IsNil。
用 Go 1.27.1 的基准测试与汇编量一次类型断言的成本:具体类型断言只是比较类型字,耗时与 1.95 ns 的空函数调用基线几乎相同;接口类型断言调用 runtime.typeAssert,多约 0.15–0.45 ns;类型开关先比 hash 再比类型地址,8 个 case 命中第 1 个与第 8 个没有差别;断言不分配内存。
用 Go 1.27.1 读字符串的 16 字节头并实测转换:子串与原串共用内存,string(b) 与 []byte(s) 默认拷贝,unsafe.String 不拷贝而写字面量会 SIGBUS;不逃逸的 string(b) 在 32 字节以内零分配,[]byte(s) 在结果从不被修改时由编译器“zero-copy”(1000 字节也是 0 次,-d=zerocopy=0 关掉后变成 1 次);m[string(b)] 与 string(b) == s 零分配;10 MiB 字符串只留 5 字节的子串会让堆保持 10.20 MiB。
用 Go 1.27.1 实测字符串的字节与 rune:range 的下标是字节偏移、遍历得到 rune,非法 UTF-8 变成 U+FFFD,按字节截断会切坏字符,预组合 é 与 e+重音符号 == 为 false,国旗与家庭 emoji 的 rune 数不等于看到的字符数;1000 个汉字 range 2222 ns 对逐字节 887 ns,先转 []rune 5016 ns;go vet 对 string(int) 的警告。
用 Go 1.27.1 实测 append 的扩容:小于 256 翻倍、之后按 (旧容量+768)/4 增长,再向上取整到内存 size class,所以 []int 的容量是 4 8 … 512 848 1280 1792 2560;不逃逸的 append 第一次直接用 32 字节栈缓冲(追加 4 个 int 零分配),逃逸时从 1 开始;一次追加多个时按需要的长度取整;100 万个 int 从 nil 逐个 append 换 36 次底层数组、累计分配 39.8 MiB,预分配只需 7.6 MiB。
用 Go 1.27.1 实测切片共享底层数组:子切片的写入会改到原切片,容量够时 append 原地写、悄悄改掉原切片后面的元素,容量不够才换新数组;三下标切片、slices.Clone 与 copy 能切断共享;函数里的 append 在容量够时会写到调用者的底层数组;复用缓冲区时存子切片会让所有结果变成最后一次的内容。
用 Go 1.27.1 实测数组的值语义:赋值、传参、结构体里的数组都会拷贝;range 数组会先拷贝(循环里改元素 v 看不到),range 切片或数组指针看得到;数组可以 == 和做 map 的键,长度不同是不同类型;传 32 KiB 数组按值 2039 ns、传指针 1200 ns;切片与切片不能比较、不能做键。
在 Go 1.27.1 里实测 map 的扩容:map 已是 Swiss Table 实现(旧的渐进式搬迁已移除),插入第 9 个键离开单组小 map,装载率 7/8 时容量依次翻倍,满 1024 个槽的表后拆分;100 万个键不给容量提示时 p99.9 是 16125 ns、最大 80 µs,给容量提示 p99.9 是 1500 ns;delete 全部键内存不回收,clear(m) 才回到 0.20 MiB。