Go 1.19 发行说明
Go 1.19 简介
最新的 Go 版本 1.19,在 Go 1.18 发布五个月后到来。它的主要变化集中在工具链、运行时和库的实现上。和往常一样,此次发布保持了 Go 1 兼容性承诺。我们预计几乎所有 Go 程序都能像以前一样继续编译和运行。
语言变化
语言方面只有一个小改动,即对方法声明中类型参数的范围进行了非常小的修正。现有程序不受影响。
内存模型
Go 内存模型已修订,以使 Go 与 C、C++、Java、JavaScript、Rust 和 Swift 使用的内存模型保持一致。Go 只提供顺序一致的原子操作,而不提供其他语言中更宽松的形式。随着内存模型的更新,Go 1.19 在 sync/atomic
包中引入了新类型,使原子值更容易使用,例如 atomic.Int64
和 atomic.Pointer[T]
。
移植
LoongArch 64 位
Go 1.19 添加了对 Linux 上龙芯 64 位架构 LoongArch 的支持(GOOS=linux
,GOARCH=loong64
)。实现的 ABI 是 LP64D。支持的最低内核版本是 5.19。
请注意,大多数现有的商用 LoongArch Linux 发行版都带有较旧的内核,其系统调用 ABI 在历史上不兼容。编译的二进制文件在这些系统上将无法工作,即使是静态链接的也不行。此类不受支持系统上的用户仅限于使用发行版提供的 Go 包。
RISC-V
riscv64
端口现在支持使用寄存器传递函数参数和结果。基准测试显示 riscv64
上的典型性能提升达到 10% 或更多。
工具
文档注释
Go 1.19 添加了对文档注释中链接、列表和更清晰标题的支持。作为此更改的一部分,gofmt
现在会重新格式化文档注释,使其呈现的含义更清晰。有关语法详细信息和 gofmt
现在突出显示常见错误的描述,请参阅“Go 文档注释”。作为此更改的另一部分,新包 go/doc/comment
提供文档注释的解析和重新格式化,以及支持将其渲染为 HTML、Markdown 和文本。
新的 unix
构建约束
//go:build
行现在可以识别构建约束 unix
。如果目标操作系统(也称为 GOOS
)是 Unix 或类 Unix 系统,则该约束满足。对于 1.19 版本,如果 GOOS
是 aix
、android
、darwin
、dragonfly
、freebsd
、hurd
、illumos
、ios
、linux
、netbsd
、openbsd
或 solaris
之一,则该约束满足。在未来的版本中,unix
约束可能会匹配更多新支持的操作系统。
Go 命令
-trimpath
标志(如果设置)现在包含在 go build
注入 Go 二进制文件的构建设置中,可以使用 go version -m
或 debug.ReadBuildInfo
进行检查。
go generate
现在在生成器的环境中明确设置 GOROOT
环境变量,以便生成器即使在使用 -trimpath
构建时也能找到正确的 GOROOT
。
go test
和 go generate
现在将 GOROOT/bin
放在用于子进程的 PATH
的开头,因此执行 go
命令的测试和生成器会将其解析到相同的 GOROOT
。
go env
现在会引用其报告的 CGO_CFLAGS
、CGO_CPPFLAGS
、CGO_CXXFLAGS
、CGO_FFLAGS
、CGO_LDFLAGS
和 GOGCCFLAGS
变量中包含空格的条目。
go list -json
现在接受以逗号分隔的 JSON 字段列表来填充。如果指定了列表,则 JSON 输出将仅包含这些字段,并且 go list
可能会避免计算未包含的字段。在某些情况下,这可能会抑制原本会报告的错误。
go
命令现在缓存加载某些模块所需的信息,这应该会加快一些 go list
的调用速度。
Vet
vet
检查器“errorsas”现在报告当 errors.As
以 *error
类型的第二个参数调用时(一个常见的错误)。
运行时
运行时现在支持软内存限制。此内存限制包括 Go 堆和运行时管理的所有其他内存,不包括外部内存源,例如二进制文件本身的映射、其他语言管理的内存以及操作系统为 Go 程序持有的内存。此限制可以通过 runtime/debug.SetMemoryLimit
或等效的 GOMEMLIMIT
环境变量进行管理。该限制与 runtime/debug.SetGCPercent
/ GOGC
协同工作,即使 GOGC=off
也会被遵守,从而允许 Go 程序始终最大化地利用其内存限制,在某些情况下提高资源效率。有关更详细的软内存限制以及各种常见用例和场景的详细指南,请参阅 GC 指南。请注意,由于外部延迟因素(例如操作系统调度),较小的内存限制(例如几十兆字节或更少)不太可能被遵守。有关更多详细信息,请参阅 issue 52433。较大的内存限制(例如几百兆字节或更多)是稳定的并且已准备好投入生产。
为了限制程序实时堆大小接近软内存限制时 GC 抖动的影响,Go 运行时还尝试将 GC CPU 总利用率限制在 50%(不包括空闲时间),选择使用更多内存而不是阻止应用程序进程。实际上,我们预计此限制仅在特殊情况下发挥作用,新的 运行时指标 /gc/limiter/last-enabled:gc-cycle
报告上次发生此情况的时间。
当应用程序空闲到足以强制进行周期性 GC 循环时,运行时现在会在空闲操作系统线程上调度更少的 GC 工作 goroutine。
运行时现在将根据 goroutine 历史平均堆栈使用情况分配初始 goroutine 堆栈。这避免了平均情况下一些早期堆栈增长和复制的需求,但会为低于平均水平的 goroutine 浪费最多 2 倍的空间。
在 Unix 操作系统上,导入 os 包的 Go 程序现在会自动将打开文件限制(RLIMIT_NOFILE
)增加到允许的最大值;也就是说,它们将软限制更改为与硬限制匹配。这纠正了某些系统上为与使用 select 系统调用的非常旧的 C 程序兼容而设置的人为低限制。Go 程序不受该限制的帮助,相反,即使是像 gofmt
这样的简单程序在并行处理许多文件时也经常在这些系统上耗尽文件描述符。此更改的一个影响是,反过来在子进程中执行非常旧的 C 程序的 Go 程序可能会以过高的限制运行这些程序。这可以通过在调用 Go 程序之前设置硬限制来纠正。
不可恢复的致命错误(例如并发映射写入或未锁定互斥锁的解锁)现在会打印一个更简单的堆栈跟踪,不包括运行时元数据(等同于致命 panic),除非 GOTRACEBACK=system
或 crash
。运行时内部致命错误堆栈跟踪始终包含完整的元数据,无论 GOTRACEBACK
的值如何。
ARM64 上已添加对调试器注入函数调用的支持,使用户在使用更新的调试器时,可以在交互式调试会话中从其二进制文件中调用函数,以利用此功能。
Go 1.18 中添加的地址消毒器支持现在更精确地处理函数参数和全局变量。
编译器
编译器现在使用跳转表来实现大型整数和字符串 switch 语句。switch 语句的性能改进各不相同,但可以达到 20% 的速度提升。(仅限 GOARCH=amd64
和 GOARCH=arm64
)
Go 编译器现在需要 -p=importpath
标志来构建可链接的目标文件。这已由 go
命令和 Bazel 提供。任何其他直接调用 Go 编译器的构建系统也需要确保传递此标志。
Go 编译器不再接受 -importmap
标志。直接调用 Go 编译器的构建系统必须改用 -importcfg
标志。
汇编器
与编译器一样,汇编器现在需要 -p=importpath
标志来构建可链接的目标文件。这已由 go
命令提供。任何其他直接调用 Go 汇编器的构建系统也需要确保传递此标志。
链接器
在 ELF 平台上,链接器现在以标准 gABI 格式(SHF_COMPRESSED
)发出压缩的 DWARF 节,而不是旧版 .zdebug
格式。
标准库
新的原子类型
sync/atomic
包定义了新的原子类型 Bool
、Int32
、Int64
、Uint32
、Uint64
、Uintptr
和 Pointer
。这些类型隐藏了底层值,以便所有访问都强制使用原子 API。Pointer
也避免了在调用站点转换为 unsafe.Pointer
的需要。Int64
和 Uint64
在结构体和分配的数据中自动对齐到 64 位边界,即使在 32 位系统上也是如此。
PATH 查找
Command
和 LookPath
不再允许从 PATH 搜索中找到相对于当前目录的结果。这消除了常见的安全问题来源,但也可能破坏依赖于使用 exec.Command("prog")
来运行当前目录中名为 prog
(或在 Windows 上,prog.exe
)的二进制文件的现有程序。有关如何最好地更新此类程序的信息,请参阅 os/exec
包文档。
在 Windows 上,Command
和 LookPath
现在遵守 NoDefaultCurrentDirectoryInExePath
环境变量,使得可以在 Windows 系统上禁用 PATH 查找中默认隐式搜索“.
”
对库的微小更改
一如既往,库中进行了各种小的更改和更新,始终牢记 Go 1 的兼容性承诺。还有各种未在此处列出的性能改进。
archive/zip
Reader
现在会忽略 ZIP 文件开头的非 ZIP 数据,与大多数其他实现匹配。这对于读取某些 Java JAR 文件等用途是必需的。
crypto/elliptic
在无效曲线点上操作(即 IsOnCurve
方法返回 false,且从未由 Unmarshal
或对有效点操作的 Curve
方法返回的点)一直都是未定义行为,并可能导致密钥恢复攻击。如果将无效点提供给 Marshal
、MarshalCompressed
、Add
、Double
或 ScalarMult
,它们现在将 panic。
在 P224
、P384
和 P521
曲线上的 ScalarBaseMult
操作现在快了三倍,导致某些 ECDSA 操作的速度也有类似提升。通用的(非平台优化)P256
实现已替换为来自经过正式验证模型的实现;这可能会导致 32 位平台上的显着减速。
crypto/rand
Read
不再在调用之间缓冲从操作系统获取的随机数据。以高频率执行许多小读取的应用程序可能会选择将 Reader
封装在 bufio.Reader
中以提高性能,同时注意使用 io.ReadFull
以确保不会发生部分读取。
在 Plan 9 上,Read
已重新实现,用快速密钥擦除生成器替换了 ANSI X9.31 算法。
Prime
实现已更改为仅使用拒绝采样,这消除了在非加密上下文中生成小素数时的偏差,消除了一个可能的小计时泄漏,并使行为与 BoringSSL 更好地对齐,同时简化了实现。与以前的实现相比,此更改会为给定的随机源流生成不同的输出,这可能会破坏预期特定确定性随机源产生特定结果的测试。为了帮助防止将来出现此类问题,该实现现在有意地对输入流不确定。
crypto/tls
GODEBUG
选项 tls10default=1
已移除。仍然可以通过设置 Config.MinVersion
来启用 TLS 1.0 客户端。
TLS 服务器和客户端现在拒绝 TLS 握手中的重复扩展,如 RFC 5246 第 7.4.1.4 节和 RFC 8446 第 4.2 节所要求。
crypto/x509
CreateCertificate
不再支持创建 SignatureAlgorithm
设置为 MD5WithRSA
的证书。
CreateCertificate
不再接受负序列号。
当生成的证书没有扩展时,CreateCertificate
不再发出空的 SEQUENCE。
原计划在 Go 1.19 中移除的 GODEBUG
选项 x509sha1=1
已重新安排到未来的版本。使用它的应用程序应着手迁移。自 2017 年以来已证明对 SHA-1 的实际攻击,并且自 2015 年以来,公共信任的证书颁发机构已不再颁发 SHA-1 证书。
ParseCertificate
和 ParseCertificateRequest
现在拒绝包含重复扩展的证书和 CSR。
新的 CertPool.Clone
和 CertPool.Equal
方法分别允许克隆 CertPool
和检查两个 CertPool
的等效性。
新的函数 ParseRevocationList
提供了一个更快、更安全的 CRL 解析器,它返回一个 RevocationList
。解析 CRL 还会填充新的 RevocationList
字段 RawIssuer
、Signature
、AuthorityKeyId
和 Extensions
,这些字段被 CreateRevocationList
忽略。
新的方法 RevocationList.CheckSignatureFrom
检查 CRL 上的签名是否是来自 Certificate
的有效签名。
ParseCRL
和 ParseDERCRL
函数现在已弃用,取而代之的是 ParseRevocationList
。Certificate.CheckCRLSignature
方法已弃用,取而代之的是 RevocationList.CheckSignatureFrom
。
Certificate.Verify
的路径构建器已 overhauled,现在应该可以在复杂场景中生成更好的链和/或更高效。名称约束现在也对非叶证书强制执行。
crypto/x509/pkix
类型 CertificateList
和 TBSCertificateList
已弃用。应改用新的 crypto/x509
CRL 功能。
debug/elf
新的 EM_LOONGARCH
和 R_LARCH_*
常量支持 loong64 端口。
debug/pe
新的 File.COFFSymbolReadSectionDefAux
方法,返回一个 COFFSymbolAuxFormat5
,提供了对 PE 文件节中 COMDAT 信息的访问。这些都由新的 IMAGE_COMDAT_*
和 IMAGE_SCN_*
常量支持。
encoding/binary
新的接口 AppendByteOrder
提供了将 uint16
、uint32
或 uint64
高效地附加到字节切片的方法。BigEndian
和 LittleEndian
现在实现了此接口。
类似地,新的函数 AppendUvarint
和 AppendVarint
是 PutUvarint
和 PutVarint
的高效附加版本。
encoding/csv
新的方法 Reader.InputOffset
报告读取器的当前输入位置作为字节偏移量,类似于 encoding/json
的 Decoder.InputOffset
。
encoding/xml
新的方法 Decoder.InputPos
报告读取器的当前输入位置作为行和列,类似于 encoding/csv
的 Decoder.FieldPos
。
flag
新的函数 TextVar
定义了一个带有实现 encoding.TextUnmarshaler
的值的标志,允许命令行标志变量具有 big.Int
、netip.Addr
和 time.Time
等类型。
fmt
新的函数 Append
、Appendf
和 Appendln
将格式化数据附加到字节切片。
go/parser
解析器现在将 ~x
识别为带运算符 token.TILDE
的一元表达式,允许在不正确的上下文中使用类型约束(例如 ~int
)时更好地进行错误恢复。
go/types
新的方法 Func.Origin
和 Var.Origin
返回在类型实例化期间创建的合成 Func
和 Var
对象的通用类型的相应 Object
。
通过递归调用 Named.Underlying
或 Named.Method
,不再可能生成无限数量的不同但相同的 Named
类型实例化。
hash/maphash
新的函数 Bytes
和 String
提供了一种高效的方法来哈希单个字节切片或字符串。它们等同于使用更通用的 Hash
进行单次写入,但它们避免了小输入的设置开销。
html/template
类型 FuncMap
现在是 text/template
的 FuncMap
的别名,而不是其自己的命名类型。这允许编写在任一设置下操作 FuncMap
的代码。
Go 1.19.8 及更高版本禁止 ECMAScript 6 模板字面量中的操作。此行为可以通过 GODEBUG=jstmpllitinterp=1
设置还原。
image/draw
当目标和源图像都是 image.NRGBA
或都是 image.NRGBA64
时,使用 Src
运算符的 Draw
会保留非预乘 alpha 颜色。这恢复了 Go 1.18 库优化意外引入的行为更改;代码现在与 Go 1.17 及更早版本中的行为匹配。
io
NopCloser
的结果现在实现了 WriterTo
,只要其输入实现了。
MultiReader
的结果现在无条件地实现了 WriterTo
。如果任何底层读取器未实现 WriterTo
,则会进行适当模拟。
mime
仅在 Windows 上,mime 包现在会忽略记录扩展名 .js
应具有 MIME 类型 text/plain
的注册表条目。这是 Windows 系统上常见的无意配置错误。其结果是 .js
将具有默认的 MIME 类型 text/javascript; charset=utf-8
。在 Windows 上期望 text/plain
的应用程序现在必须明确调用 AddExtensionType
。
mime/multipart
在 Go 1.19.8 及更高版本中,此包设置了其处理的 MIME 数据的大小限制,以防止恶意输入。Reader.NextPart
和 Reader.NextRawPart
将部分中的头数量限制为 10000,Reader.ReadForm
将所有 FileHeaders
中的头总数限制为 10000。这些限制可以通过 GODEBUG=multipartmaxheaders
设置进行调整。Reader.ReadForm
进一步将表单中的部分数量限制为 1000。此限制可以通过 GODEBUG=multipartmaxparts
设置进行调整。
net
纯 Go 解析器现在将使用 EDNS(0) 来包含建议的最大回复数据包长度,允许回复数据包包含多达 1232 字节(之前的最大值为 512)。万一这导致本地 DNS 解析器出现问题,设置环境变量 GODEBUG=netdns=cgo
以使用基于 cgo 的解析器应该可以解决问题。请在问题跟踪器上报告任何此类问题。
当 net 包函数或方法返回“I/O timeout”错误时,该错误现在将满足 errors.Is(err, context.DeadlineExceeded)
。当 net 包函数返回“operation was canceled”错误时,该错误现在将满足 errors.Is(err, context.Canceled)
。这些更改旨在使代码更容易测试上下文取消或超时导致 net 包函数或方法返回错误的情况,同时保持错误消息的向后兼容性。
Resolver.PreferGo
现在已在 Windows 和 Plan 9 上实现。它以前仅适用于 Unix 平台。结合 Dialer.Resolver
和 Resolver.Dial
,现在可以编写可移植程序并在拨号时控制所有 DNS 名称查找。
net
包现在在 Windows 上初步支持 netgo
构建标签。使用时,该包使用 Go DNS 客户端(如 Resolver.PreferGo
所用),而不是向 Windows 请求 DNS 结果。然而,它从 Windows 发现的上游 DNS 服务器在复杂的系统网络配置下可能还不正确。
net/http
ResponseWriter.WriteHeader
现在支持发送用户定义的 1xx 信息性头。
MaxBytesReader
返回的 io.ReadCloser
现在在其读取限制超出时将返回定义的错误类型 MaxBytesError
。
HTTP 客户端将处理没有 Location
头部的 3xx 响应,将其返回给调用者,而不是将其视为错误。
net/url
新的 JoinPath
函数和 URL.JoinPath
方法通过连接路径元素列表来创建新的 URL
。
URL
类型现在区分没有 authority 的 URL 和带有空 authority 的 URL。例如,http:///path
有一个空 authority(主机),而 http:/path
没有。
当 URL
具有空 authority 时,新的 URL
字段 OmitHost
设置为 true
。
os/exec
具有非空 Dir
字段和 nil Env
的 Cmd
现在隐式为子进程设置 PWD
环境变量以匹配 Dir
。
新的方法 Cmd.Environ
报告将用于运行命令的环境,包括隐式设置的 PWD
变量。
reflect
方法 Value.Bytes
现在除了切片之外还接受可寻址数组。
方法 Value.Len
和 Value.Cap
现在可以成功地对数组指针进行操作并返回该数组的长度,以匹配 内置 len
和 cap
函数的行为。
regexp/syntax
Go 1.18 发布候选版本 1、Go 1.17.8 和 Go 1.16.15 包含对正则表达式解析器的安全修复,使其拒绝非常深层嵌套的表达式。由于 Go 补丁发布不引入新的 API,在这种情况下解析器返回 syntax.ErrInternalError
。Go 1.19 添加了一个更具体的错误 syntax.ErrNestingDepth
,解析器现在返回该错误。
runtime
当二进制文件使用 -trimpath
标志构建且进程环境中未设置 GOROOT
变量时,GOROOT
函数现在返回空字符串(而不是 "go"
)。
runtime/metrics
新的 /sched/gomaxprocs:threads
指标报告当前的 runtime.GOMAXPROCS
值。
新的 /cgo/go-to-c-calls:calls
指标报告从 Go 调用 C 的总次数。此指标与 runtime.NumCgoCall
函数相同。
新的 /gc/limiter/last-enabled:gc-cycle
指标报告上次启用 GC CPU 限制器的 GC 周期。有关 GC CPU 限制器的详细信息,请参阅运行时说明。
runtime/pprof
在收集 goroutine 配置文件时,停止世界暂停时间已显着减少,从而降低了对应用程序的整体延迟影响。
现在,所有 Unix 操作系统都在堆配置文件中报告 MaxRSS
(以前仅在 GOOS=android
、darwin
、ios
和 linux
中报告)。
runtime/race
除了 windows/amd64
和 openbsd/amd64
(仍保留在 v2 上)之外,所有支持的平台上的竞争检测器都已升级到使用线程消毒器 v3。与 v2 相比,它现在通常快 1.5 到 2 倍,内存使用量减半,并且支持无限数量的 goroutine。在 Linux 上,竞争检测器现在至少需要 glibc 版本 2.17 和 GNU binutils 2.26。
竞争检测器现在支持 GOARCH=s390x
。
对 openbsd/amd64
的竞争检测器支持已从上游线程消毒器中移除,因此不太可能从 v2 更新。
runtime/trace
当同时启用跟踪和 CPU 探查器时,执行跟踪将包含 CPU 探查器样本作为瞬时事件。
sort
排序算法已重写为使用 模式消除快速排序,这在几种常见场景中速度更快。
新的函数 Find
类似于 Search
,但通常更容易使用:它返回一个额外的布尔值,报告是否找到了相等的值。
strconv
Quote
和相关函数现在将 Unicode 字符 U+007F 引用为 \x7f
,而不是 \u007f
,以与其他 ASCII 值保持一致。
syscall
在 PowerPC(GOARCH=ppc64
、ppc64le
)上,Syscall
、Syscall6
、RawSyscall
和 RawSyscall6
现在始终为返回值 r2
返回 0,而不是未定义的值。
在 AIX 和 Solaris 上,Getrusage
现在已定义。
time
新的方法 Duration.Abs
提供了一种方便且安全的方式来获取持续时间的绝对值,将 −2⁶³ 转换为 2⁶³−1。(这种边界情况可能由于从零时间减去最近的时间而发生。)
新的方法 Time.ZoneBounds
返回在给定时间生效的时区的开始和结束时间。它可以在循环中使用,以枚举给定位置所有已知的时区转换。