Go 1.26 新特性解析:new 表达式、现代化 go fix 与新 GC
· 436 words · ~ 3 min read
Last modified:
Go 1.26 发布于 2026-02-10。它同时改了语言、工具链和运行时:new 更好用,泛型约束表达力更强,go fix 被现代化,新 GC 默认启用。
源码侧 api/go1.26.txt 有 180 条公开 API 增量;公开标准库目录新增 crypto/hpke、crypto/mlkem/mlkemtest、runtime/secret、simd/archsimd、testing/cryptotest。
主要变化
1. new 可以直接接收表达式
Go 1.26 扩展了内置 new:除了 new(T) 创建零值指针,也可以用 new(expr) 直接基于表达式结果分配并初始化变量。
|
|
这类写法特别适合 encoding/json、protobuf 这类用指针表达“可选字段”的场景。过去通常要先写临时变量再取地址;现在可以直接在字面量里构造带初值的指针。
2. 泛型约束允许自引用
泛型类型参数可以在约束里引用当前泛型类型本身。它适合表达“同类值之间的操作”,例如可加、可比较、可合并的自类型接口。
|
|
这类约束的意义是:方法参数或返回值必须和实现类型保持同一族类型。数值封装、向量、矩阵、不可变集合这类 API 更容易表达“两个同类型值相加得到同类型值”。
3. go fix 变成 modernizers 平台
新的 go fix 基于 Go analysis 框架,能够承载更现代的源码改写规则。它不再只是早期 Go 版本遗留的修复工具,而是可以围绕标准库 API 演进、过时代码模式和工具建议做结构化改写。
modernizer 的关键是“按语法树改代码”,不是简单字符串替换。它可以理解导入、选择器、类型信息和作用域,因此适合把旧 idiom 改成新 API。例如把手写的 helper 换成标准库函数、把旧的组合调用换成更明确的新 API。Go 1.26 还引入源码级 inliner,用于把某些 API 包装层自动内联成目标写法。
4. Green Tea GC 默认启用
Go 1.26 默认启用 Green Tea GC。它延续 Go 1.25 的实验方向,目标是改善垃圾回收标记阶段的局部性和吞吐表现。同时,实验性 goroutine leak profile 提供观察 goroutine 泄漏的新入口。
goroutine 泄漏 profile 关注的是“哪些 goroutine 长期存在且没有退出”。这类问题经常来自 channel 等待、context 没有取消、后台循环缺少退出条件。profile 能把泄漏排查从人工看 goroutine dump 推向更结构化的诊断。
它的判定机制不是“等太久就算泄漏”,而是基于 GC 可达性:如果 goroutine G 阻塞在某个并发原语 P 上,而 P 对任何可运行 goroutine 都已经不可达,那么 runtime 就能判断 P 不可能再被唤醒,于是 G 属于高置信度泄漏。启用方式仍然是构建时设置 GOEXPERIMENT=goroutineleakprofile;启用后,除了 runtime/pprof 里的 goroutineleak profile,net/http/pprof 也会暴露 /debug/pprof/goroutineleak 端点。
这项能力也有边界:如果 goroutine 卡住的同步原语仍然能从全局变量、或者某个可运行 goroutine 的局部变量间接触达,runtime 就不能把它判成“必然泄漏”。所以它更像一个高精度新探针,而不是对所有永久阻塞场景都 100% 覆盖的 oracle。
语言与规范
Go 1.26 的语言层面变化虽然不算很多,但有两个很实用。第一,new 现在可以接收表达式操作数,直接把表达式结果变成带初值的指针,这对可选字段和测试构造都很顺手。第二,泛型类型可以在自己的类型参数列表中引用自身,类似 type Adder[A Adder[A]] interface { Add(A) A } 的模式现在合法。
工具链与运行时
go fix 被彻底改造,成为基于 Go analysis 框架的现代化修复工具。它不只是历史遗留修复器,而是可以承载现代 API 演进和源码级 inline directive 的平台。
go mod init 在 Go 1.26 系列里默认生成较低一个大版本的 go 行。例如用 Go 1.26.x 创建新模块,默认得到的是 go 1.25.0,而不是 go 1.26.0。这个设计是为了鼓励新模块默认声明为“兼容当前仍受支持的较低版本”。如果确实要把新模块直接声明为更高版本,可以在 go mod init 后继续执行 go get go@version。cmd/doc 和 go tool doc 删除,使用 go doc 替代。
这个 go mod init 行为变化很具体,但它不是 go1.26.0 的一次性实验,而是 Go 1.26 系列的默认规则。release notes 还特别说明了预发布版本会再低一个大版本:例如 Go 1.26 的 RC 默认生成 go 1.24.0,正式版和后续小版本默认生成 go 1.25.0。
运行时方面,Green Tea GC 从 Go 1.25 实验变为默认启用。cgo 调用基础开销降低,64 位平台堆基址随机化增强安全性。GOEXPERIMENT=goroutineleakprofile 可启用实验性 goroutine 泄漏 profile。
性能相关还有三点值得单独看。第一,Green Tea GC 官方预期在大量使用 GC 的真实程序中降低 10%-40% 的 GC 开销,具体收益取决于对象形态和分配模式。第二,cgo 调用的基础运行时开销降低约 30%,这对频繁跨 Go/C 边界的数据库、图形、系统库绑定更明显。第三,编译器能在更多场景把 slice backing store 分配到栈上,减少短生命周期切片给堆和 GC 带来的压力。
标准库与新增包
新增公开包:
crypto/hpke:实现 RFC 9180 HPKE,含后量子混合 KEM 支持。crypto/mlkem/mlkemtest:ML-KEM 已知答案测试辅助。testing/cryptotest:密码学测试辅助。simd/archsimd:实验性架构相关 SIMD。runtime/secret:实验性敏感临时数据擦除能力。
重要变化:
bytes.Buffer.Peek可无推进查看缓冲区内容。errors.AsType提供泛型版errors.As。crypto/tls默认启用后量子混合密钥交换。net/http/httputil.ReverseProxy.Director被废弃,推荐Rewrite。
bytes.Buffer.Peek 能简化解析器和协议处理代码。过去想看缓冲区前 N 个字节又不消费内容,常见做法是 b.Bytes()[:n],但这需要自己做长度判断,也容易暴露可变底层切片。Peek 把“检查长度 + 返回前缀”做成一个明确 API。
errors.AsType[T] 是泛型版 errors.As。过去要先声明目标变量,再传指针:
|
|
有了泛型版后,调用可以更直接地表达“我要从错误链里取出这个类型”。这类 API 对错误处理 helper 和测试断言尤其方便。
小版本特殊变化
Go 1.26 系列截至 2026-05-07 有 3 个小版本。这里最值得单独说的是:go mod init 默认写低一个大版本的行为不是 go1.26.0 的临时变化,而是整个 Go 1.26 系列共享的默认规则。
安全修复方面,Go 1.26 的前三个小版本集中在三条线。第一是供应链和工具链:go 命令、compiler、pack 工具都在安全修复范围内,说明构建、打包、模块处理本身也需要按安全边界看待。第二是 Web 和文本协议:html/template、net/http、net/http/httputil、net/mail、net/url,这些包关系到模板转义、请求解析、代理转发、邮件地址解析和 URL 规范化。第三是加密和系统边界:crypto/tls、crypto/x509、crypto/fips140、os、syscall。这些修复通常不会表现为业务 API 变化,但会改变底层验证、系统调用或合规路径的正确性。
go1.26.0:release notes 写明go mod init会默认写入较低的go行,例如 Go 1.26 创建go 1.25.0;RC 版本会再低一个大版本。go1.26.1:延续 Go 1.26 的整体方向,继续包含go fix、compiler、reflect 等 Bug 修复,以及crypto/x509、html/template、net/url、os安全修复。go1.26.2:安全修复覆盖go命令、compiler、archive/tar、crypto/tls、crypto/x509、html/template、os;Bug 修复覆盖go命令、go fix、compiler、linker、runtime、net、net/http、net/url。go1.26.3:安全修复覆盖go命令、pack工具、html/template、net、net/http、net/http/httputil、net/mail、syscall;Bug 修复覆盖go fix、compiler、linker、runtime、crypto/fips140、crypto/tls、go/types、os。
所以这篇文章里对 go mod init 的判断不该按小版本拆开理解:它是 Go 1.26 系列稳定保留的默认行为,不是只在 go1.26.0 出现过一次。