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 添加了对 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
是以下之一,则该约束将得到满足: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 指南 以获取详细指南,解释更详细的软内存限制,以及各种常见用例和场景。请注意,由于外部延迟因素(如操作系统调度),较小的内存限制(大约几十兆字节或更小)不太可能得到尊重。请参阅 问题 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=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
) 而不是传统的.zdebug
格式输出压缩的 DWARF 部分。
标准库
新的原子类型
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。
GODEBUG
选项x509sha1=1
的删除,最初计划在 Go 1.19 中进行,已被重新安排到未来的版本中。使用它的应用程序应该着手迁移。自从 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
的路径构建器已经过大修,现在应该在复杂的场景中产生更好的链或更有效。名称约束现在也在非叶证书上执行。
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
返回泛型类型的相应 Object
,用于在类型实例化期间创建的合成 Func
和 Var
对象。
现在不再可能通过对 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
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.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 超时”错误时,该错误现在将满足 errors.Is(err, context.DeadlineExceeded)
。当 net 包函数返回“操作被取消”错误时,该错误现在将满足 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
类型现在区分没有权威性的 URL 和具有空权威性的 URL。例如,http:///path
具有空权威性(主机),而 http:/path
没有权威性。
新的 URL
字段 OmitHost
在 URL
具有空权威性时设置为 true
。
os/exec
具有非空 Dir
字段和空 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 说明。
runtime/pprof
在收集 goroutine 配置文件时,停止世界暂停时间已大幅减少,从而降低了对应用程序的整体延迟影响。
现在在所有 Unix 操作系统上的堆配置文件中报告 MaxRSS
(以前只针对 GOOS=android
、darwin
、ios
和 linux
报告)。
runtime/race
竞速检测器已升级为在所有支持的平台上使用线程消毒器版本 v3,除了 windows/amd64
和 openbsd/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=ppc64
、ppc64le
)上,Syscall
、Syscall6
、RawSyscall
和 RawSyscall6
现在始终为返回值 r2
返回 0,而不是未定义的值。
在 AIX 和 Solaris 上,现在定义了 Getrusage
。
time
新方法 Duration.Abs
提供了一种便捷而安全的方式来获取持续时间的绝对值,将 -2⁶³ 转换为 2⁶³-1。(这种边界情况可能是由于从零时间减去最近的时间而发生的。)
新方法 Time.ZoneBounds
返回给定时间生效时区的开始和结束时间。它可以在循环中使用,以枚举给定位置的所有已知时区转换。