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.Int64atomic.Pointer[T])变得更加容易。

移植

LoongArch 64 位

Go 1.19 添加了对 Loongson 64 位架构 LoongArch 在 Linux (GOOS=linux, GOARCH=loong64) 上的支持。实现的 ABI 是 LP64D。支持的最低内核版本为 5.19。

请注意,大多数现有的 LoongArch 商业 Linux 发行版都带有较旧的内核,这些内核具有历史上的不兼容系统调用 ABI。即使静态链接,编译后的二进制文件也无法在这些系统上运行。在这些不受支持的系统上的用户只能使用发行版提供的 Go 包。

RISC-V

riscv64 端口现在支持使用寄存器传递函数参数和结果。基准测试表明,riscv64 上的典型性能提升了 10% 或更多。

工具

文档注释

Go 1.19 添加了对文档注释中链接、列表和更清晰的标题的支持。作为此更改的一部分,gofmt 现在会重新格式化文档注释,使其呈现的含义更加清晰。请参阅“Go 文档注释”以了解语法详细信息和 gofmt 现在突出显示的常见错误的描述。作为此更改的另一个部分,新的软件包 go/doc/comment 提供了对文档注释的解析和重新格式化,以及将其渲染为 HTML、Markdown 和文本的支持。

新的 unix 构建约束

构建约束 unix 现在在 //go:build 行中得到识别。如果目标操作系统(也称为 GOOS)是 Unix 或类 Unix 系统,则该约束将得到满足。对于 1.19 版本,如果 GOOS 是以下之一,则该约束将得到满足:aixandroiddarwindragonflyfreebsdhurdillumosioslinuxnetbsdopenbsdsolaris。在将来的版本中,unix 约束可能与其他新支持的操作系统匹配。

Go 命令

如果设置了 -trimpath 标志,现在将将其包含在 go build 标记到 Go 二进制文件中的构建设置中,可以使用 go version -mdebug.ReadBuildInfo 进行检查。

go generate 现在在生成器的环境中显式设置 GOROOT 环境变量,以便即使使用 -trimpath 构建,生成器也可以找到正确的 GOROOT

go testgo generate 现在将 GOROOT/bin 放在子进程使用的 PATH 的开头,因此执行 go 命令的测试和生成器将将其解析为相同的 GOROOT

go env 现在会引用它报告的 CGO_CFLAGSCGO_CPPFLAGSCGO_CXXFLAGSCGO_FFLAGSCGO_LDFLAGSGOGCCFLAGS 变量中包含空格的条目。

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 指南 以获取详细指南,解释更详细的软内存限制,以及各种常见用例和场景。请注意,由于外部延迟因素(如操作系统调度),较小的内存限制(大约几十兆字节或更小)不太可能得到尊重。请参阅 问题 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 程序之前设置硬限制来纠正。

不可恢复的致命错误(例如并发映射写入或未锁定的互斥锁的解锁)现在会打印一个更简单的回溯,不包括运行时元数据(等效于致命恐慌),除非 GOTRACEBACK=systemcrash。无论 GOTRACEBACK 的值如何,运行时内部致命错误回溯始终包含完整的元数据。

已在 ARM64 上添加了对调试器注入的函数调用的支持,使用户能够在使用更新以利用此功能的调试器时,在交互式调试会话中从其二进制文件中调用函数。

Go 1.18 中添加的 地址清理程序支持 现在更精确地处理函数参数和全局变量。

编译器

编译器现在使用跳转表来实现大整数和字符串 switch 语句。switch 语句的性能改进差异很大,但可以快 20%。 (仅适用于GOARCH=amd64GOARCH=arm64)

Go 编译器现在需要-p=importpath 标志来构建可链接的目标文件。go 命令和 Bazel 已经提供了这个标志。任何直接调用 Go 编译器的其他构建系统都需要确保也传递此标志。

Go 编译器不再接受-importmap 标志。直接调用 Go 编译器的构建系统必须使用-importcfg 标志。

汇编器

与编译器类似,汇编器现在需要-p=importpath 标志来构建可链接的目标文件。go 命令已经提供了这个标志。任何直接调用 Go 汇编器的其他构建系统都需要确保也传递此标志。

链接器

在 ELF 平台上,链接器现在会以标准 gABI 格式 (SHF_COMPRESSED) 而不是传统的.zdebug 格式输出压缩的 DWARF 部分。

标准库

新的原子类型

sync/atomic 包定义了新的原子类型 BoolInt32Int64Uint32Uint64UintptrPointer。这些类型隐藏了底层值,因此所有访问都强制使用原子 API。 Pointer 还避免了在调用站点转换为 unsafe.Pointer 的需要。 Int64Uint64 在结构体和分配的数据中自动对齐到 64 位边界,即使在 32 位系统上也是如此。

PATH 查找

CommandLookPath 不再允许从 PATH 搜索中找到相对于当前目录的结果。这消除了一个常见的安全问题来源,但也可能破坏依赖于使用,例如exec.Command("prog") 在当前目录中运行名为prog (或在 Windows 上,prog.exe) 的二进制文件的现有程序。有关如何最好地更新此类程序的信息,请参阅os/exec 包文档。

在 Windows 上,CommandLookPath 现在尊重NoDefaultCurrentDirectoryInExePath 环境变量,从而可以禁用 Windows 系统上 PATH 查找中对“.”的默认隐式搜索。

库的微小更改

与往常一样,库中有一些细微的更改和更新,这些更改和更新都是为了遵循 Go 1兼容性承诺。还有一些性能改进,这里没有列出。

archive/zip

Reader 现在忽略 ZIP 文件开头处的非 ZIP 数据,与大多数其他实现匹配。这是读取一些 Java JAR 文件所必需的,以及其他用途。

crypto/elliptic

在无效曲线点 (IsOnCurve 方法返回 false 的点,以及从Unmarshal 或在有效点上运行的Curve 方法永远不会返回的点) 上进行操作始终是未定义的行为,并且可能导致密钥恢复攻击。如果向 MarshalMarshalCompressedAddDoubleScalarMult 提供无效点,它们现在会 panic。

P224P384P521 曲线上进行的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。

GODEBUG 选项x509sha1=1 的删除,最初计划在 Go 1.19 中进行,已被重新安排到未来的版本中。使用它的应用程序应该着手迁移。自从 2017 年以来,针对 SHA-1 的实际攻击已经得到证明,并且自 2015 年以来,公共信任证书颁发机构不再颁发 SHA-1 证书。

ParseCertificateParseCertificateRequest 现在会拒绝包含重复扩展的证书和 CSR。

新的 CertPool.CloneCertPool.Equal 方法分别允许克隆CertPool 和检查两个CertPool 的等效性。

新的函数 ParseRevocationList 提供了一个更快、更安全的 CRL 解析器,它返回一个 RevocationList。解析 CRL 还会填充新的RevocationList 字段RawIssuerSignatureAuthorityKeyIdExtensions,这些字段被 CreateRevocationList 忽略。

新的方法 RevocationList.CheckSignatureFrom 检查 CRL 上的签名是否是来自 Certificate 的有效签名。

ParseCRLParseDERCRL 函数现在已被弃用,建议使用ParseRevocationListCertificate.CheckCRLSignature 方法已被弃用,建议使用RevocationList.CheckSignatureFrom

Certificate.Verify 的路径构建器已经过大修,现在应该在复杂的场景中产生更好的链或更有效。名称约束现在也在非叶证书上执行。

crypto/x509/pkix

类型 CertificateListTBSCertificateList 已被弃用。建议使用新的 crypto/x509 CRL 功能

debug/elf

新的EM_LOONGARCHR_LARCH_* 常量支持 loong64 端口。

debug/pe

新的 File.COFFSymbolReadSectionDefAux 方法,它返回一个 COFFSymbolAuxFormat5,提供了对 PE 文件部分中 COMDAT 信息的访问。这些由新的IMAGE_COMDAT_*IMAGE_SCN_* 常量支持。

encoding/binary

新的接口 AppendByteOrder 提供了有效的方法来将uint16uint32uint64 附加到字节切片。BigEndianLittleEndian 现在实现了此接口。

类似地,新的函数 AppendUvarintAppendVarintPutUvarintPutVarint 的有效附加版本。

encoding/csv

新的方法 Reader.InputOffset 将读取器的当前输入位置报告为字节偏移,类似于encoding/jsonDecoder.InputOffset

encoding/xml

新的方法 Decoder.InputPos 将读取器的当前输入位置报告为行和列,类似于encoding/csvDecoder.FieldPos

flag

新的函数 TextVar 定义了一个具有实现 encoding.TextUnmarshaler 的值的标志,允许命令行标志变量具有诸如 big.Intnetip.Addrtime.Time 之类的类型。

fmt

新的函数 AppendAppendfAppendln 将格式化的数据追加到字节切片。

go/parser

解析器现在识别~x 作为带有操作符 token.TILDE 的一元表达式,当类型约束(如 ~int)在错误上下文中使用时,可以实现更好的错误恢复。

go/types

新方法 Func.OriginVar.Origin 返回泛型类型的相应 Object,用于在类型实例化期间创建的合成 FuncVar 对象。

现在不再可能通过对 Named.UnderlyingNamed.Method 的递归调用,生成无限数量的独特但相同的 Named 类型实例化。

hash/maphash

新函数 BytesString 提供了一种高效的方式来哈希单个字节切片或字符串。它们等同于使用更通用的 Hash 进行单次写入,但它们避免了对小输入的设置开销。

html/template

类型 FuncMap 现在是 text/templateFuncMap 的别名,而不是它自己的命名类型。这允许编写在任一设置中对 FuncMap 进行操作的代码。

Go 1.19.8 及更高版本 不允许在 ECMAScript 6 模板字面量中执行操作。 此行为可以通过 GODEBUG=jstmpllitinterp=1 设置来恢复。

image/draw

Draw 使用 Src 操作符在目标和源图像都是 image.NRGBA 或都是 image.NRGBA64 时,保留非预乘 alpha 颜色。这恢复了 Go 1.18 库优化意外引入的行为变化;代码现在与 Go 1.17 及更早版本的行为一致。

io

NopCloser 的结果现在在它的输入实现 WriterTo 时,也实现 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.NextPartReader.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 超时”错误时,该错误现在将满足 errors.Is(err, context.DeadlineExceeded)。当 net 包函数返回“操作被取消”错误时,该错误现在将满足 errors.Is(err, context.Canceled)。这些更改旨在使代码更容易测试上下文取消或超时导致 net 包函数或方法返回错误的情况,同时保持错误消息的向后兼容性。

Resolver.PreferGo 现在在 Windows 和 Plan 9 上实现。以前它只在 Unix 平台上工作。结合 Dialer.ResolverResolver.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 类型现在区分没有权威性的 URL 和具有空权威性的 URL。例如,http:///path 具有空权威性(主机),而 http:/path 没有权威性。

新的 URL 字段 OmitHostURL 具有空权威性时设置为 true

os/exec

具有非空 Dir 字段和空 EnvCmd 现在会隐式地为子进程设置 PWD 环境变量,使其与 Dir 相匹配。

新方法 Cmd.Environ 报告运行命令时将使用的环境,包括隐式设置的 PWD 变量。

reflect

方法 Value.Bytes 现在除了切片之外,还接受可寻址的数组。

方法 Value.LenValue.Cap 现在可以成功地在指向数组的指针上操作,并返回该数组的长度,以匹配 内置 lencap 函数的行为

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 说明

runtime/pprof

在收集 goroutine 配置文件时,停止世界暂停时间已大幅减少,从而降低了对应用程序的整体延迟影响。

现在在所有 Unix 操作系统上的堆配置文件中报告 MaxRSS(以前只针对 GOOS=androiddarwinioslinux 报告)。

runtime/race

竞速检测器已升级为在所有支持的平台上使用线程消毒器版本 v3,除了 windows/amd64openbsd/amd64,它们仍然使用 v2。与 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 和相关函数现在将 rune U+007F 引用为 \x7f,而不是 \u007f,以与其他 ASCII 值保持一致。

syscall

在 PowerPC(GOARCH=ppc64ppc64le)上,SyscallSyscall6RawSyscallRawSyscall6 现在始终为返回值 r2 返回 0,而不是未定义的值。

在 AIX 和 Solaris 上,现在定义了 Getrusage

time

新方法 Duration.Abs 提供了一种便捷而安全的方式来获取持续时间的绝对值,将 -2⁶³ 转换为 2⁶³-1。(这种边界情况可能是由于从零时间减去最近的时间而发生的。)

新方法 Time.ZoneBounds 返回给定时间生效时区的开始和结束时间。它可以在循环中使用,以枚举给定位置的所有已知时区转换。