Go 1.15 版本说明

Go 1.15 简介

最新的 Go 版本 1.15 在 Go 1.14 发布六个月后发布。其大部分更改都体现在工具链、运行时和库的实现中。与往常一样,此版本维护了 Go 1 的 兼容性承诺。我们预计几乎所有 Go 程序都将像以前一样继续编译和运行。

Go 1.15 包括 对链接器的大量改进,改进了 在高核心数下分配小型对象,并弃用了 X.509 通用名称GOPROXY 现在支持跳过返回错误的代理,并添加了一个新的 嵌入式 tzdata 软件包

语言更改

语言没有更改。

端口

Darwin

如 Go 1.14 版本说明中 所述,Go 1.15 需要 macOS 10.12 Sierra 或更高版本;对先前版本的支持已停止。

如 Go 1.14 版本说明中 所述,Go 1.15 停止支持 macOS、iOS、iPadOS、watchOS 和 tvOS 上的 32 位二进制文件(darwin/386darwin/arm 端口)。Go 继续支持 64 位 darwin/amd64darwin/arm64 端口。

Windows

当提供 -buildmode=pie cmd/link 标志时,Go 现在会生成 Windows ASLR 可执行文件。Go 命令在 Windows 上默认使用 -buildmode=pie

-race-msan 标志现在始终启用 -d=checkptr,它会检查 unsafe.Pointer 的用法。以前,除了 Windows 之外的所有操作系统都是这种情况。

Go 构建的 DLL 在收到信号(例如终端中的 Ctrl-C)时不再导致进程退出。

Android

在为 Android 链接二进制文件时,Go 1.15 会显式选择最近版本的 NDK 中提供的 lld 链接器。lld 链接器避免了某些设备上的崩溃,并计划在未来的 NDK 版本中成为默认的 NDK 链接器。

OpenBSD

Go 1.15 添加了对 GOARCH=armGOARCH=arm64 上的 OpenBSD 6.7 的支持。以前版本的 Go 已经在 GOARCH=386GOARCH=amd64 上支持 OpenBSD 6.7。

RISC-V

在改进 Linux 上 64 位 RISC-V 端口(GOOS=linuxGOARCH=riscv64)的稳定性和性能方面取得了进展。它现在还支持异步抢占。

386

Go 1.15 是最后一个支持仅 x87 浮点硬件(GO386=387)的版本。将来的版本将至少需要 SSE2 支持才能在 386 上运行,从而将 Go 的最低 GOARCH=386 要求提高到英特尔奔腾 4(2000 年发布)或 AMD Opteron/Athlon 64(2003 年发布)。

工具

Go 命令

GOPROXY 环境变量现在支持跳过返回错误的代理。代理 URL 现在可以使用逗号 (,) 或管道字符 (|) 分隔。如果代理 URL 后跟一个逗号,则 go 命令仅会在收到 404 或 410 HTTP 响应后尝试列表中的下一个代理。如果代理 URL 后跟一个管道字符,则 go 命令将在任何错误后尝试列表中的下一个代理。请注意,GOPROXY 的默认值仍然是 https://proxy.golang.org,direct,在发生错误时不会回退到 direct

go test

更改 -timeout 标志现在会使缓存的测试结果失效。当 go test 再次使用较短的超时时间调用时,具有较长超时时间的测试运行的缓存结果将不再被视为通过。

标志解析

已修复 go testgo vet 中的各种标志解析问题。值得注意的是,GOFLAGS 中指定的标志处理更加一致,并且 -outputdir 标志现在将相对于 go 命令的工作目录(而不是每个单独测试的工作目录)解释相对路径。

模块缓存

现在可以使用 GOMODCACHE 环境变量设置模块缓存的位置。GOMODCACHE 的默认值为 GOPATH[0]/pkg/mod,即此更改之前模块缓存的位置。

现在可以使用一种解决方法来解决 Windows 中访问模块缓存的 go 命令出现的“拒绝访问”错误,这些错误是由外部程序同时扫描文件系统引起的(请参阅 问题 #36568)。默认情况下不会启用此解决方法,因为当低于 1.14.2 和 1.13.10 的 Go 版本与同一模块缓存同时运行时,使用它是不安全的。可以通过显式设置环境变量 GODEBUG=modcacheunzipinplace=1 来启用它。

Vet

针对 string(x) 的新警告

vet 工具现在会警告 string(x) 形式的转换,其中 x 的整数类型不是 runebyte。Go 的使用经验表明,许多此类转换错误地假设 string(x) 会计算为整数 x 的字符串表示形式。它实际上会计算为包含 x 值的 UTF-8 编码的字符串。例如,string(9786) 不会计算为字符串 "9786";它会计算为字符串 "\xe2\x98\xba",或 "☺"

正确使用 string(x) 的代码可以重写为 string(rune(x))。或者,在某些情况下,使用合适的字节切片 buf 调用 utf8.EncodeRune(buf, x) 可能是正确的解决方案。其他代码最有可能使用 strconv.Itoafmt.Sprint

使用 go test 时,此新的 vet 检查默认启用。

我们正在考虑在将来的 Go 版本中禁止此转换。也就是说,语言将更改为仅在 x 的类型为 runebyte 时允许对整数 x 使用 string(x)。这样的语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步尝试。

针对不可能的接口转换的新警告

当类型断言始终失败时,vet 工具现在会警告从一个接口类型到另一个接口类型的类型断言。如果两种接口类型都实现了一个具有相同名称但类型签名不同的方法,则会发生这种情况。

没有理由编写始终失败的类型断言,因此应重写触发此 vet 检查的任何代码。

使用 go test 时,此新的 vet 检查默认启用。

我们正在考虑在将来的 Go 版本中禁止不可能的接口类型断言。这样的语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步尝试。

运行时

如果 panic 使用类型派生自以下任何类型的值为参数调用:boolcomplex64complex128float32float64intint8int16int32int64stringuintuint8uint16uint32uint64uintptr,则会打印该值,而不仅仅是其地址。以前,这仅适用于这些类型的精确值。

在 Unix 系统上,如果 kill 命令或 kill 系统调用用于向 Go 程序发送 SIGSEGVSIGBUSSIGFPE 信号,并且如果未通过 os/signal.Notify 处理该信号,则 Go 程序现在将可靠地崩溃并显示堆栈跟踪。在早期版本中,行为是不可预测的。

小型对象的分配现在在高核心数下性能要好得多,并且最坏情况下的延迟更低。

将一个小整数转换为接口值不再会导致分配。

已关闭通道上的非阻塞接收现在与开放通道上的非阻塞接收的性能一样好。

编译器

软件包 unsafe安全规则 允许在调用某些函数时将 unsafe.Pointer 转换为 uintptr。以前,在某些情况下,编译器允许进行多次链接转换(例如,syscall.Syscall(…, uintptr(uintptr(ptr)), …))。编译器现在要求恰好进行一次转换。应更新使用多次转换的代码以满足安全规则。

通过消除某些类型的 GC 元数据并更积极地消除未使用的类型元数据,Go 1.15 将典型的二进制文件大小减少了约 5%(与 Go 1.14 相比)。

工具链现在通过将函数对齐到 32 字节边界并填充跳转指令来缓解 GOARCH=amd64 上的 英特尔 CPU 勘误 SKX102。虽然此填充会增加二进制文件的大小,但这可以通过上面提到的二进制文件大小改进来弥补。

Go 1.15 向编译器和汇编器都添加了 -spectre 标志,以允许启用 Spectre 缓解措施。这些措施几乎永远不需要,主要作为一种“纵深防御”机制提供。有关详细信息,请参阅 Spectre Wiki 页面

编译器现在会拒绝对应用于其声明没有意义的 //go: 编译器指令,并显示“错位的编译器指令”错误。此类误用指令以前已损坏,但编译器会静默忽略。

编译器的 -json 优化日志记录现在报告大型(>= 128 字节)复制并包含逃逸分析决策的说明。

链接器

此版本包括对 Go 链接器的重大改进,这些改进减少了链接器资源的使用(时间和内存)并提高了代码的健壮性和可维护性。

对于一组有代表性的大型 Go 程序,链接速度提高了 20%,内存使用量平均减少了 30%,适用于在 amd64 架构上运行的基于 ELF 的操作系统(Linux、FreeBSD、NetBSD、OpenBSD、Dragonfly 和 Solaris),其他架构/操作系统组合的改进幅度较小。

链接器性能提升的关键因素是重新设计的对象文件格式以及内部阶段的改进以提高并发性(例如,并行地将重定位应用于符号)。Go 1.15 中的对象文件略大于 1.14 中的等效文件。

这些更改是 使 Go 链接器现代化的多版本项目的一部分,这意味着将来版本中将会有更多链接器改进。

链接器现在在linux/amd64linux/arm64上,对于-buildmode=pie默认使用内部链接模式,因此这些配置不再需要C链接器。外部链接模式(在Go 1.14中,对于-buildmode=pie是默认模式)仍然可以通过-ldflags=-linkmode=external标志来请求。

Objdump

objdump工具现在支持使用-gnu标志以GNU汇编语法进行反汇编。

标准库

新的嵌入式tzdata包

Go 1.15包含一个新的包,time/tzdata,它允许将时区数据库嵌入到程序中。导入此包(如import _ "time/tzdata")允许程序即使在本地系统上没有时区数据库的情况下也能找到时区信息。您也可以通过使用-tags timetzdata构建来嵌入时区数据库。这两种方法都会使程序大小增加约800 KB。

Cgo

Go 1.15将把C类型EGLConfig转换为Go类型uintptr。此更改类似于Go 1.12及更高版本处理EGLDisplay、Darwin的CoreFoundation和Java的JNI类型的方式。有关更多信息,请参阅cgo文档

在Go 1.15.3及更高版本中,cgo不允许Go代码在栈或堆上分配未定义的结构体类型(仅定义为struct S;或类似的C结构体)。Go代码只能使用指向这些类型的指针。分配此类结构体的实例并将指针或完整的结构体值传递给C代码始终是不安全的,并且不太可能正常工作;现在禁止这样做。解决方法是:要么重写Go代码以仅使用指针,要么通过包含相应的C头文件来确保Go代码可以看到结构体的完整定义。

X.509 CommonName弃用

当没有主题备用名称时,将X.509证书上的CommonName字段视为主机名的已弃用旧行为现在默认已禁用。可以通过将值x509ignoreCN=0添加到GODEBUG环境变量中来临时重新启用它。

请注意,如果CommonName是一个无效的主机名,则无论GODEBUG设置如何,它都会被忽略。无效名称包括包含字母、数字、连字符和下划线以外的任何字符的名称,以及包含空标签或尾随点的名称。

库的细微更改

与往常一样,对库进行了各种细微的更改和更新,并牢记Go 1的兼容性承诺

bufio

Scanner与一个无效的io.Reader一起使用时,该ReaderRead中错误地返回了一个负数,Scanner将不再出现panic,而是返回新的错误ErrBadReadCount

context

使用空父级创建派生Context现在被明确禁止。使用WithValueWithDeadlineWithCancel函数尝试这样做会导致panic。

crypto

crypto/rsacrypto/ecdsacrypto/ed25519包中的PrivateKeyPublicKey类型现在具有一个Equal方法,用于比较密钥的等价性或为公钥创建类型安全的接口。该方法签名与go-cmp的等价性定义兼容。

Hash现在实现了fmt.Stringer

crypto/ecdsa

新的SignASN1VerifyASN1函数允许以标准ASN.1 DER编码生成和验证ECDSA签名。

crypto/elliptic

新的MarshalCompressedUnmarshalCompressed函数允许以压缩格式编码和解码NIST椭圆曲线点。

crypto/rsa

VerifyPKCS1v15现在根据RFC 8017拒绝缺少前导零的无效短签名。

crypto/tls

新的Dialer类型及其DialContext方法允许使用上下文来连接并与TLS服务器进行握手。

Config类型上的新VerifyConnection回调允许为每个连接使用自定义验证逻辑。它可以访问ConnectionState,其中包括对等证书、SCT和已钉扎的OCSP响应。

现在每24小时自动轮换一次自动生成的会话票证密钥,有效期为7天,以限制它们对前向保密的影响。

TLS 1.2及更早版本中会话票证的生存期(其中会话密钥被重复用于恢复的连接)现在限制为7天,同样是为了限制它们对前向保密的影响。

现在强制执行RFC 8446中指定的客户端降级保护检查。这可能会导致客户端遇到充当未经授权的降级攻击的中介时出现连接错误。

SignatureSchemeCurveIDClientAuthType现在实现了fmt.Stringer

ConnectionState字段OCSPResponseSignedCertificateTimestamps现在在客户端恢复的连接上重新填充。

tls.Conn现在在永久断开的连接上返回一个不透明的错误,并包装临时net.Error。要访问原始net.Error,请使用errors.As(或errors.Unwrap)而不是类型断言。

crypto/x509

如果证书上的名称或正在验证的名称(使用VerifyOptions.DNSNameVerifyHostname)无效,则现在将不区分大小写地进行比较,而无需进一步处理(不遵守通配符或去除尾随点)。无效名称包括包含字母、数字、连字符和下划线以外的任何字符的名称,包含空标签的名称,以及证书上带有尾随点的名称。

新的CreateRevocationList函数和RevocationList类型允许创建符合RFC 5280的X.509 v2证书吊销列表。

CreateCertificate现在如果模板是CA并且没有明确指定,则会自动生成SubjectKeyId

CreateCertificate现在如果模板指定了MaxPathLen但不是CA,则会返回错误。

在macOS以外的Unix系统上,SSL_CERT_DIR环境变量现在可以是冒号分隔的列表。

在macOS上,二进制文件现在始终与Security.framework链接以提取系统信任根,而不管cgo是否可用。结果行为应该与OS验证器更加一致。

crypto/x509/pkix

Name.String现在如果ExtraNames为空,则打印来自Names的非标准属性。

database/sql

新的DB.SetConnMaxIdleTime方法允许在连接空闲一段时间后将其从连接池中移除,而不管连接的总生命周期如何。DBStats.MaxIdleTimeClosed字段显示由于DB.SetConnMaxIdleTime而关闭的连接总数。

新的Row.Err获取器允许在不调用Row.Scan的情况下检查查询错误。

database/sql/driver

Conn可以实现新的Validator接口,以允许驱动程序发出信号指示连接是否有效或是否应该被丢弃。

debug/pe

该包现在定义了PE文件格式使用的IMAGE_FILEIMAGE_SUBSYSTEMIMAGE_DLLCHARACTERISTICS常量。

encoding/asn1

Marshal现在根据X.690 DER对SET OF的组件进行排序。

Unmarshal现在拒绝根据X.690 DER未进行最小编码的标签和对象标识符。

encoding/json

该包现在对解码时的嵌套最大深度有一个内部限制。这减少了深度嵌套的输入可能使用大量栈内存,甚至导致“goroutine stack exceeds limit” panic的可能性。

flag

flag包看到-h-help时,并且这些标志未定义,它现在会打印一条使用消息。如果FlagSet是用ExitOnError创建的,则FlagSet.Parse将以状态2退出。在此版本中,-h-help的退出状态已更改为0。特别是,这适用于命令行标志的默认处理。

fmt

打印动词%#g%#G现在保留浮点值的尾随零。

go/format

SourceNode函数现在将数字文字前缀和指数规范化为Go源代码格式化的一部分。这与gofmt命令的行为一致,该命令已自Go 1.13以来实现。

html/template

该包现在在所有JavaScript和JSON上下文中使用Unicode转义符(\uNNNN)。这修复了application/ld+jsonapplication/json上下文中转义错误。

io/ioutil

TempDirTempFile现在拒绝包含路径分隔符的模式。也就是说,诸如ioutil.TempFile("/tmp", "../base*")之类的调用将不再成功。这可以防止意外的目录遍历。

math/big

新的Int.FillBytes方法允许序列化到固定大小的预分配字节切片。

math/cmplx

此包中的函数已更新为符合C99标准(附录G IEC 60559兼容复数运算),关于处理特殊参数(如无穷大、NaN和带符号的零)。

net

如果I/O操作超过了Conn.SetDeadlineConn.SetReadDeadlineConn.SetWriteDeadline方法设置的截止日期,它现在将返回一个错误,该错误是或包装了os.ErrDeadlineExceeded。这可用于可靠地检测错误是否由于超过截止日期所致。早期版本建议在错误上调用Timeout方法,但I/O操作可能会返回错误,对于这些错误,Timeout返回true,尽管截止日期尚未超过。

新的 Resolver.LookupIP 方法支持特定网络的 IP 查询,并接受上下文。

net/http

作为抵御请求走私攻击的加固措施,解析现在更加严格:非 ASCII 空格不再像 SP 和 HTAB 一样被修剪,并且放弃了对“identityTransfer-Encoding 的支持。

net/http/httputil

ReverseProxy 现在支持在传入的 Request.Header 映射条目中该字段为 nil 时不修改 X-Forwarded-For 标头。

当由 ReverseProxy 处理的切换协议(如 WebSocket)请求被取消时,后端连接现在会正确关闭。

net/http/pprof

所有配置文件端点现在都支持“seconds”参数。如果存在,端点将在指定秒数内生成配置文件并报告差异。“seconds”参数在 cpu 配置文件和跟踪端点中的含义保持不变。

net/url

新的 URL 字段 RawFragment 和方法 EscapedFragment 提供了有关特定片段的精确编码的详细信息并对其进行控制。它们类似于 RawPathEscapedPath

新的 URL 方法 Redacted 以字符串形式返回 URL,其中任何密码都被替换为 xxxxx

os

如果 I/O 操作超过了由 File.SetDeadlineFile.SetReadDeadlineFile.SetWriteDeadline 方法设置的截止时间,它现在将返回一个错误,该错误是或包含 os.ErrDeadlineExceeded。这可用于可靠地检测错误是否由于超过截止时间导致。早期版本建议在错误上调用 Timeout 方法,但 I/O 操作可能会返回错误,对于这些错误,即使未超过截止时间,Timeout 也返回 true

osnet 包现在会自动重试因 EINTR 失败的系统调用。以前这会导致虚假错误,随着 Go 1.14 中添加了异步抢占,这种情况变得更加普遍。现在,这将被透明地处理。

os.File 类型现在支持 ReadFrom 方法。当使用 io.Copy 将数据从一个 os.File 复制到另一个 os.File 时,这允许在某些系统上使用 copy_file_range 系统调用。结果是,当复制到 os.File 时,io.CopyBuffer 并不总是使用提供的缓冲区。如果程序希望强制使用提供的缓冲区,可以通过编写 io.CopyBuffer(struct{ io.Writer }{dst}, src, buf) 来实现。

plugin

现在在 macOS 上支持(并默认启用)-buildmode=plugin 的 DWARF 生成。

现在在 freebsd/amd64 上支持使用 -buildmode=plugin 构建。

reflect

reflect 包现在不允许访问所有非导出字段的方法,而之前它允许访问非导出嵌入字段的方法。依赖于先前行为的代码应更新为改为访问封闭变量的相应提升方法。

regexp

新的 Regexp.SubexpIndex 方法返回正则表达式中具有给定名称的第一个子表达式的索引。

runtime

包括 ReadMemStatsGoroutineProfile 在内的几个函数,如果垃圾回收正在进行,则不再阻塞。

runtime/pprof

goroutine 配置文件现在包括与在分析时每个 goroutine 关联的配置文件标签。此功能尚未在 debug=2 报告的配置文件中实现。

strconv

添加了 FormatComplexParseComplex 用于处理复数。

FormatComplex 将复数转换为 (a+bi) 形式的字符串,其中 a 和 b 分别是实部和虚部。

ParseComplex 将字符串转换为指定精度的复数。ParseComplex 接受 N+Ni 格式的复数。

sync

新方法 Map.LoadAndDelete 原子地删除键并返回先前存在的值(如果存在)。

Map.Delete 方法效率更高。

syscall

在 Unix 系统上,使用 SysProcAttr 的函数现在将拒绝尝试同时设置 SetcttyForeground 字段,因为它们都使用 Ctty 字段,但使用方法不兼容。我们预计很少有现有程序同时设置这两个字段。

设置 Setctty 字段现在要求将 Ctty 字段设置为子进程中的文件描述符编号,该编号由 ProcAttr.Files 字段确定。使用子描述符始终有效,但在某些情况下,使用父文件描述符也碰巧有效。一些设置 Setctty 的程序需要更改 Ctty 的值以使用子描述符编号。

现在可以在 windows/amd64 上调用返回浮点值的系统调用。

testing

testing.T 类型现在具有 Deadline 方法,该方法报告测试二进制文件超过超时的时间。

TestMain 函数不再需要调用 os.Exit。如果 TestMain 函数返回,测试二进制文件将使用 m.Run 返回的值调用 os.Exit

新方法 T.TempDirB.TempDir 返回在测试结束时自动清理的临时目录。

go test -v 现在按测试名称对输出进行分组,而不是在每一行上打印测试名称。

text/template

JSEscape 现在始终使用 Unicode 转义序列 (\u00XX),这与 JSON 兼容。

time

新方法 Ticker.Reset 支持更改滴答器的持续时间。

在返回错误时,ParseDuration 现在引用原始值。