本文主要翻译自 Go 1.19 Release Notes - The Go Programming Language。
最新的 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]。
Go 1.19 在 Linux (GOOS=linux, GOARCH=loong64)上增加了对龙芯 64 位架构 LoongArch 的支持。实现的 ABI 是 LP64D。支持的最低内核版本为 5.19。
请注意,大多数现有的 LoongArch 商业 Linux 发行版都带有较旧的内核,具有历史上不兼容的系统调用 ABI。即使静态链接,编译后的二进制文件也无法在这些系统上运行。此类不受支持的系统上的用户仅限于分发提供的 Go 包。
该 riscv64 端口现在支持使用寄存器传递函数参数和结果。基准测试表明,在 riscv64 上的典型性能提高了10%或更多。
Go 1.19 在文档注释中添加了对链接、列表和更清晰标题的支持。作为此更改的一部分,gofmt 现在重新格式化文档注释以使其呈现的含义更清晰。请参阅 “Go Doc Comments” 以获取语法详细信息和现在 gofmt 突出显示的常见错误的描述。作为此更改的另一部分,新包 go/doc/comment 提供了对文档注释的解析和重新格式化,并支持将它们呈现为 HTML、Markdown 和文本。
unix 现在可以按//go:build
行识别构建约束。如果目标操作系统(也称为 GOOS)是 Unix 或类 Unix 系统,则满足约束。对于 1.19 版本,如果 GOOS 是 aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd 或 solaris 中的一个则满足 。在未来的版本中,该 unix 约束可能与其他新支持的操作系统相匹配。
-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 调用的速度。
当调用 errors.As 第二个参数使用 *error 类型的参数时,这是一个常见错误,vet 检查器 “errorsas” 现在会报告错误。
运行时现在包括对软内存限制的支持。 此内存限制包括 Go 堆和运行时管理的所有其他内存,不包括外部内存源,例如二进制文件本身的映射、其他语言管理的内存以及操作系统代表 Go 程序持有的内存。此限制可以通过 runtime/debug.SetMemoryLimit 或等效的GOMEMLIMIT 环境变量来管理。该限制与 runtime/debug.SetGCPercent / GOGC 结合使用,即使 GOGC=off 也会生效,允许 Go 程序始终最大限度地利用其内存限制,在某些情况下提高了资源效率。有关更详细地解释软内存限制以及各种常见用例和场景的详细指南,请参阅 GC 指南。请注意,由于外部延迟因素(例如 OS 调度),较小的内存限制(大约数十兆字节或更小)不太可能会生效。有关详细信息,请参阅 issue 52433。更大的内存限制,大约数百兆字节或更多,是稳定的和生产就绪的。
为了在程序的活动堆大小接近软内存限制时限制 GC 抖动的影响,Go 运行时还尝试将总 GC CPU 利用率限制为 50%,不包括空闲时间,选择使用更多内存而不是阻止应用程序执行。在实践中,我们希望此限制仅在特殊情况下发挥作用,并且新的运行时指标 /gc/limiter/last-enabled:gc-cycle 会报告最后一次发生的时间。
当应用程序空闲到足以强制执行周期性 GC 循环时,运行时现在在空闲操作系统线程上调度更少的 GC worker goroutines。
运行时现在将根据 goroutine 的历史平均堆栈使用情况分配初始 goroutine 堆栈。这避免了在平均情况下需要的一些早期堆栈增长和复制,但在低于平均水平的 goroutines 上最多会有 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) 发出压缩的 DWARF 部分,而不是传统 .zdebug 格式。
该 sync/atomic 包定义了新的原子类型 Bool, Int32, Int64, Uint32, Uint64, Uintptr 和 Pointer。这些类型隐藏了底层值,因此所有访问都被迫使用原子 API。Pointer 还避免了在调用处转换为 unsafe.Pointer 的需要。 Int64 和 Uint64 在结构和分配的数据中自动对齐到 64 位边界,即使在 32 位系统上也是如此。
Command 和 LookPath 不再允许相对于当前目录找到 PATH 搜索的结果。这消除了一个常见的安全问题来源, 但也可能破坏现有的程序,这些程序依赖于在当前目录使用exec.Command("prog")
中运行名为 prog(或在 Windows 上叫 prog.exe)的二进制文件。有关如何最好地更新此类程序的信息,请参阅 os/exec 包文档。
在 Windows 上,Command 和 LookPath 现在尊重 NoDefaultCurrentDirectoryInExePath 环境变量,从而可以禁用 Windows 系统上 PATH 查找时 “.” 的隐式默认搜索。
与往常一样,在考虑到 Go 1 的兼容性承诺的情况下,对库进行了各种细微的更改和更新。 还有各种性能提升,这里就不一一列举了。
Reader 现在忽略 ZIP 文件开头的非 ZIP 数据,与大多数其他实现匹配。这是读取一些 Java JAR 文件以及其他用途所必需的。
对无效曲线点( IsOnCurve 方法返回 false 且 Unmarshal 或 Curve 在有效点上操作从来不会返回的点)的操作,一直是未定义的行为,并且可能导致密钥恢复攻击。如果向 Marshal、 MarshalCompressed、 Add、 Double 或 ScalarMult 提供无效点,它们现在会发生 panic。
P224、P384 和 P521 曲线上的 ScalarBaseMult 运算现在速度提高了三倍,从而在某些 ECDSA 运算中实现了类似的加速。通用的(非平台优化的)P256 实现被一个从正式验证的模型派生的模型取代;这可能会导致 32 位平台的显着减速。
Read 不再缓冲调用之间从操作系统获得的随机数据。出于性能原因,以高频执行许多小读取的应用程序可能会选择将 Reader 包装在 bufio.Reader 中,注意使用 io.ReadFull 以确保不会发生部分读取。
在 Plan 9 中,Read 已重新实现,用快速密钥擦除生成器替换 ANSI X9.31 算法。
Prime 实现已更改为仅使用拒绝采样,这消除了在非加密上下文中生成小素数时的偏差,消除了一个可能的次要时序泄漏,并更好地使行为与 BoringSSL 保持一致,同时简化了实现。与之前的实现相比,更改确实会为给定的随机源流产生不同的输出,这可能会破坏那些对特定确定性随机源有预期特定结果的测试。为了防止将来出现此类问题,现在对输入流的实现有意地采用非确定性。
GODEBUG 选项 tls10default=1 已被删除。 仍然可以通过设置 Config.MinVersion 来启用 TLS 1.0 客户端。根据 RFC 5246 第 7.4.1.4 节和 RFC 8446 第 4.2 节的要求,TLS 服务器和客户端现在拒绝 TLS 握手中的重复扩展。
CreateCertificate 不再支持创建 SignatureAlgorithm 设置为 MD5WithRSA 的证书。
CreateCertificate 不再接受负序列号。
当生成的证书没有扩展时,CreateCertificate 将不再发出空的 SEQUENCE。
最初计划用于 Go 1.19 将 GODEBUG 选项 x509sha1=1 删除已重新安排到将来的版本中。 使用它的应用程序应该可以迁移。 自 2017 年以来,已经证明了针对 SHA-1 的实际攻击,并且自 2015 年以来,公众信任的证书颁发机构没有颁发 SHA-1 证书。
ParseCertificate 和 ParseCeertificateRequest 现在拒绝包含重复扩展名的证书和 CSR。
新的 CertPool.Clone 和 CertPool.Equal 方法允许克隆 CertPool 并分别检查两个 CertPool 的等价性。
新函数 ParseRevocationList 提供了一个更快、更安全的 CRL 解析器,它返回一个 RevocationList。 解析 CRL 还会填充新的 RevocationList 字段 RawIssuer、Signature、AuthorityKeyId 和 Extensions,CreateRevocationList 会忽略这些字段。
新方法 RevocationList.CheckSignatureFrom 检查 CRL 上的签名是否是来自证书的有效签名。
ParseCRL 和 ParseDERCRL 函数现已弃用,取而代之的是 ParseRevocationList。Certificate.CheckCRLSignature 方法已弃用,取而代之的是 RevocationList.CheckSignatureFrom。
Certificate.Verify 的路径构建器已经过大修,现在应该可以生成更好的链且在复杂场景中更高效。现在也对非叶子证书实施名称限制。
CertificateList 和 TBSCertificateList 类型已被弃用,应该改用新的 crypto/x509 CRL 功能。
新的 EM_LOONGARCH 和 R_LARCH_* 常量支持 long64 端口。
新的 File.COFFSymbolReadSectionDefAux 方法返回一个 COFFSymbolAuxFormat5,提供对 PE 文件部分中的 COMDAT 信息的访问。 这些由新的 IMAGE_COMDAT_* 和 IMAGE_SCN_* 常量支持。
新接口 AppendByteOrder 提供了将 uint16、uint32 或 uint64 附加到字节切片的有效方法。 BigEndian 和 LittleEndian 现在实现了这个接口。
同样,新函数 AppendUvarint 和 AppendVarint 是 PutUvarint 和 PutVarint 的高效附加版本。
新方法 Reader.InputOffset 将 reader 的当前输入位置报告为字节偏移量,类似于 encoding/json 的 Decoder.InputOffset。
新方法 Decoder.InputPos 将 reader 的当前输入位置报告为行和列,类似于 encoding/csv 的 Decoder.FieldPos。
新函数 TextVar 定义了一个带有值的 flag,通过实现 encoding.TextUnmarshaler,允许命令行标志变量具有 big.Int、netip.Addr 和 time.Time 等类型。
新函数 Append、Appendf 和 Appendln 将格式化数据追加到字节片。
解析器现在将 ~x 识别为带有运算符 token.TILDE 的一元表达式,当在不正确的上下文中使用 ~int 等类型约束时,可以更好地恢复错误。
新方法 Func.Origin 和 Var.Origin 为在类型实例化期间创建的合成 Func 和 Var 对象返回泛型类型的相应对象。
不再可能通过对 Named.Underlying 或 Named.Method 的递归调用产生无限数量的命名相同但不同的类型的实例化。
新函数 Bytes 和 String 提供了一种有效的方法来散列单个字节切片或字符串。 它们等效于使用更通用的 Hash 进行单次写入,但它们避免了小输入的设置开销。
FuncMap 类型现在是 text/template 的 FuncMap 的别名,而不是它自己的命名类型。 这允许从任一设置编写对 FuncMap 进行操作的代码。
当目标和原图像都是 image.NRGBA 或都是 image.NRGBA64 时,使用 Src 运算符绘制会保留非预乘 alpha 颜色。 这恢复了 Go 1.18 库优化意外引入的行为更改; 该代码现在与 Go 1.17 及更早版本中的行为匹配。
NopCloser 的结果现在在其输入执行时实现 WriterTo。
MultiReader 的结果现在无条件地实现了 WriterTo。 如果任何底层 reader 未实现 WriterTo,则对其进行适当模拟。
仅在 Windows 上,mime 包现在会忽略扩展名为 .js 记录应具有 text/plain MIME 类型的注册表项。这是 Windows 系统上常见的无意错误配置。效果是 .js 将具有默认的 MIME 类型 text/javascript; charset=utf-8
。在 Windows 上需要 text/plain 的应用程序现在必须显式调用 AddExtensionType。
纯 Go 解析器现在将使用 EDNS(0) 来包含建议的最大回复包长度,允许回复包最多包含 1232 个字节(之前的最大值为 512)。万一这会导致本地 DNS 解析器出现问题,将环境变量设置 GODEBUG=netdns=cgo 为使用基于 cgo 的解析器应该可以工作。请在问题跟踪器上报告任何此类问题。
当网络包函数或方法返回“I/O 超时”错误时,该错误现在将满足 errors.Is(err, context.DeadlineExceeded). 当 net 包函数返回“操作已取消”错误时,该错误现在将满足 errors.Is(err, context.Canceled). 这些更改旨在使代码更容易测试上下文取消或超时导致网络包函数或方法返回错误的情况,同时保持错误消息的向后兼容性。
Resolver.PreferGo 现在在 Windows 和 Plan 9 上实现。它以前只在 Unix 平台上工作。结合 Dialer.Resolver和 Resolver.Dial,现在可以编写可移植程序并在拨号时控制所有 DNS 名称查找。
该 net 软件包现在在 Windows 上初步支持 netgo 构建标记。使用时,该包使用 Go DNS 客户端(由 使用Resolver.PreferGo)而不是向 Windows 询问 DNS 结果。但是,它从 Windows 中发现的上游 DNS 服务器可能尚未与复杂的系统网络配置正确。
ResponseWriter.WriteHeader 现在支持发送用户定义的 1xx 信息标头。
MaxBytesReader 返回的 io.ReadCloser 现在将在超出其读取限制时返回定义的错误类型 MaxBytesError。
HTTP 客户端将没有 Location 标头的 3xx 响应返回给调用者,而不是将其视为错误。
新 JoinPath 函数和 URL.JoinPath 方法通过拼接路径元素列表来创建新的 URL。
URL 类型现在区分没有权限的 URL 和具有空权限的 URL。例如,http:///path
权限为空(主机),而http:/path
没有权限。
新 URL 字段 OmitHost 设置为 true 当 URL 具有空权限时。
具有非空 Dir 字段和 nil Env 的 Cmd 现在隐式设置子进程的 PWD 环境变量以匹配 Dir。
新方法 Cmd.Environ 报告将用于运行命令的环境,包括隐式设置的 PWD 变量。
Value.Bytes 除了切片之外, 该方法现在还接受可寻址数组。
方法 Value.Len 和 Value.Cap 现在成功地对指向数组的指针进行操作并返回该数组的长度,以匹配内置函数 len 和 cap 函数所做的事情。
Go 1.18 候选版本 1、Go 1.17.8 和 Go 1.16.15 包括对正则表达式解析器的安全修复,使其拒绝嵌套非常深的表达式。因为 Go 补丁版本没有引入新的 API,所以在这种情况下解析器返回了 syntax.ErrInternalError。 Go 1.19 添加了一个更具体的错误,syntax.ErrNestingDepth,解析器现在返回它。
当构建二进制文件时设置了 -trimpath 标志并且在进程环境中未设置 GOROOT 变量,GOROOT 函数现在返回空字符串而不是“go”。
新 /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 限制器的详细信息,请参阅运行时说明。
在收集 goroutine profiles 时,Stop-the-world 暂停时间已显着减少,从而减少了对应用程序的整体延迟影响。
MaxRSS 现在报告在所有 Unix 操作系统的 heap profiles 中(以前只报告 GOOS=android、darwin、ios 和 linux)。
竞争检测器已升级为在所有受支持的平台上使用线程清理器版本 v3,除了windows/amd64 和openbsd/amd64,它仍然在 v2 上。与 v2 相比,它现在通常快 1.5 到 2 倍,使用一半的内存,并且支持无限数量的 goroutine。在 Linux 上,竞争检测器现在至少需要 glibc 2.17 版和 GNU binutils 2.26。
竞争检测器现在支持 GOARCH=s390x。
竞争检测器对 openbsd/amd64 的支持已从上游线程清理器中删除,因此不太可能从 v2 更新。
当跟踪和 CPU 分析器同时启用时,执行跟踪包括 CPU profile 样本作为瞬时事件。
排序算法已被重写以使用模式失败的快速排序,这在几种常见情况下速度更快。
新函数 Find 类似于 Search 但通常更易于使用:它返回一个额外的布尔值,报告是否找到相等的值。
Quote和相关函数现在将字符 U+007F 引用为 \x7f,而不是 \u007f,以与其他 ASCII 值保持一致。
在 PowerPC ( GOARCH=ppc64, ppc64le) 上, Syscall, Syscall6, RawSyscall, RawSyscall6 现在总是返回 0 作为返回值r2,而不是未定义的值。
在 AIX 和 Solaris 上,Getrusage 现在已定义。
新方法 Duration.Abs 提供了一种方便且安全的方法来获取持续时间的绝对值,将 -2⁶³ 转换为 2⁶³-1。(这种边界情况可能是从零时间中减去最近时间的结果。)
新方法 Time.ZoneBounds 返回给定时间有效时区的开始和结束时间。它可以在循环中使用以枚举给定位置的所有已知时区转换。