Go 1.7 发行说明

Go 1.7 简介

最新的 Go 版本 1.7,发布于 1.6 之后六个月。其大部分更改都在工具链、运行时和库的实现中。语言规范有一处细微更改。一如既往,此版本保持了 Go 1 的兼容性承诺。我们预计几乎所有 Go 程序都将继续像以前一样编译和运行。

此版本增加了对 IBM LinuxOne 的支持更新了 x86-64 编译器后端以生成更高效的代码;包含了从 x/net 子仓库提升的 context 包,现在已在标准库中使用;并在 testing 包中增加了支持,用于创建测试和基准测试的层次结构。此版本还完成了 Go 1.5 中开始的 vendoring 支持,使其成为一项标准功能。

语言变化

此版本对语言有一处微小的更改。关于终止语句的部分澄清道,为了确定语句列表是否以终止语句结尾,“最终非空语句”被视为结尾,这与 gc 和 gccgo 编译器工具链的现有行为相匹配。在早期版本中,定义只提及“最终语句”,使得尾随空语句的效果至少不明确。go/types 包已更新以在此方面与 gc 和 gccgo 编译器工具链匹配。此更改对现有程序的正确性没有影响。

移植

Go 1.7 增加了对 macOS 10.12 Sierra 的支持。使用 Go 1.7 之前版本构建的二进制文件在 Sierra 上将无法正常工作。

Go 1.7 增加了对 Linux on z Systemslinux/s390x)的实验性移植,以及对 Plan 9 on ARM(plan9/arm)的初步移植。

Go 1.6 中增加的对 64 位 MIPS 上的 Linux(linux/mips64linux/mips64le)的实验性移植现在已完全支持 cgo 和外部链接。

对小端 64 位 PowerPC 上的 Linux(linux/ppc64le)的实验性移植现在需要 POWER8 架构或更高版本。大端 64 位 PowerPC(linux/ppc64)只需要 POWER5 架构。

OpenBSD 移植现在需要 OpenBSD 5.6 或更高版本,才能访问 getentropy(2) 系统调用。

已知问题

FreeBSD 上存在一些已知但不了解的不稳定性。这些可能在极少数情况下导致程序崩溃。请参阅问题 16136问题 15658问题 16396。任何有助于解决这些 FreeBSD 特定问题的工作都将不胜感激。

工具

汇编器

对于 64 位 ARM 系统,向量寄存器名称已更正为 V0V31;以前的版本错误地将其称为 V32V63

对于 64 位 x86 系统,已添加以下指令:PCMPESTRIRORXLRORXQVINSERTI128VPADDDVPADDQVPALIGNRVPBLENDDVPERM2F128VPERM2I128VPORVPSHUFBVPSHUFDVPSLLDVPSLLDQVPSLLQVPSRLDVPSRLDQVPSRLQ

编译器工具链

此版本包含了针对 64 位 x86 系统的新代码生成后端,遵循了 2015 年的提案,该提案自那时起一直在开发中。这个基于 SSA 的新后端生成更紧凑、更高效的代码,并为边界检查消除等优化提供了更好的平台。新后端将我们的基准程序所需的 CPU 时间减少了 5-35%。

在此版本中,可以通过向编译器传递 -ssa=0 来禁用新后端。如果您发现您的程序只有在新后端禁用后才能成功编译或运行,请提交错误报告

编译器在包存档中写入的导出元数据格式已更改:旧的文本格式已被更紧凑的二进制格式取代。这导致包存档略小,并修复了一些长期存在的边缘情况错误。

在此版本中,可以通过向编译器传递 -newexport=0 来禁用新导出格式。如果您发现您的程序只有在新导出格式禁用后才能成功编译或运行,请提交错误报告

链接器的 -X 选项不再支持不寻常的两参数形式 -X name value,如 Go 1.6 发行说明和链接器打印的警告中宣布的那样。请改用 -X name=value

此版本中编译器和链接器已进行优化,运行速度明显快于 Go 1.6,尽管它们仍然比我们期望的要慢,并将在未来版本中继续进行优化。

由于编译器工具链和标准库的更改,此版本构建的二进制文件通常应小于 Go 1.6 构建的二进制文件,有时甚至可以小 20-30%。

在 x86-64 系统上,Go 程序现在按照 Linux 的 perf 和 Intel 的 VTune 等分析工具的预期维护堆栈帧指针,使得使用这些工具分析和优化 Go 程序变得更加容易。帧指针维护会产生一个小的运行时开销,该开销因情况而异,但平均约为 2%。我们希望在未来版本中降低此成本。要构建不使用帧指针的工具链,请在运行 make.bashmake.batmake.rc 时设置 GOEXPERIMENT=noframepointer

Cgo

使用 cgo 的包现在可以包含 Fortran 源文件(除了 C、C++、Objective C 和 SWIG),尽管 Go 绑定仍然必须使用 C 语言 API。

Go 绑定现在可以使用新的辅助函数 C.CBytes。与 C.CString 接受 Go string 并返回 *C.byte(C char*)不同,C.CBytes 接受 Go []byte 并返回 unsafe.Pointer(C void*)。

在过去的版本中,由于嵌入了临时目录名称,使用 cgo 构建的包和二进制文件在每次构建时都会产生不同的输出。当此版本与足够新的 GCC 或 Clang 版本(支持 -fdebug-prefix-map 选项的版本)一起使用时,这些构建最终应该是确定性的。

Gccgo

由于 Go 的半年发布周期与 GCC 的年度发布周期保持一致,GCC 版本 6 包含了 gccgo 的 Go 1.6.1 版本。下一个版本 GCC 7 可能会包含 gccgo 的 Go 1.8 版本。

Go 命令

go 命令的基本操作没有改变,但有许多值得注意的更改。

此版本删除了对 GO15VENDOREXPERIMENT 环境变量的支持,如 Go 1.6 发行说明中宣布的那样。Vendoring 支持现在是 go 命令和工具链的标准功能。

提供给“go list”的 Package 数据结构现在包含一个 StaleReason 字段,解释了为什么某个特定包被视为过时(需要重新构建)或不被视为过时。此字段可用于 -f-json 选项,有助于理解为什么目标正在被重新构建。

go get”命令现在支持引用 git.openstack.org 的导入路径。

此版本增加了对使用仅二进制包(以二进制形式分发且不包含相应源代码的包)构建程序的实验性、最小支持。此功能在某些商业环境中是必需的,但不打算完全集成到工具链的其余部分。例如,假定可以访问完整源代码的工具将无法与此类包一起使用,并且没有计划在“go get”命令中支持此类包。

Go doc

go doc”命令现在将构造函数与其构造的类型进行分组,遵循 godoc

Go vet

go vet”命令在其 -copylock-printf 检查中具有更准确的分析,并新增了一个 -tests 检查,用于检查可能的测试函数的名称和签名。为了避免与新的 -tests 检查混淆,旧的、未公开的 -test 选项已被删除;它等效于 -all -shadow

vet 命令还新增了一个检查,-lostcancel,它检测未能调用 Go 1.7 新增的 context 包(参见下文)中的 WithCancelWithTimeoutWithDeadline 函数返回的取消函数。未能调用该函数会阻止新的 Context 被回收,直到其父上下文被取消。(后台上下文永远不会被取消。)

Go tool dist

新的子命令“go tool dist list”打印所有支持的操作系统/架构对。

Go tool trace

go tool trace”命令,在 Go 1.5 中引入,已通过各种方式进行完善。

首先,收集追踪比过去版本效率显著提高。在此版本中,收集追踪的典型执行时间开销约为 25%;在过去版本中至少为 400%。其次,追踪文件现在包含文件和行号信息,使其更加自包含,并且在运行追踪工具时原始可执行文件变为可选。第三,追踪工具现在会分解大型追踪,以避免基于浏览器的查看器中的限制。

尽管此版本中追踪文件格式已更改,但 Go 1.7 工具仍然可以读取早期版本的追踪。

性能

一如既往,更改如此普遍和多样,以至于很难对性能做出精确的陈述。由于垃圾收集器的加速和核心库的优化,大多数程序应该运行得稍快一些。在 x86-64 系统上,由于新编译器后端带来的生成代码的改进,许多程序将运行得显著更快。如上所述,在我们的基准测试中,仅代码生成更改通常就能将程序 CPU 时间减少 5-35%。

crypto/sha1crypto/sha256encoding/binaryfmthash/adler32hash/crc32hash/crc64image/colormath/bigstrconvstringsunicodeunicode/utf16 包中的实现已进行重大优化,性能提升超过 10%。

对于具有大量空闲 goroutine、堆栈大小大幅波动或大型包级变量的程序,垃圾收集暂停时间应比 Go 1.6 短得多。

标准库

Context

Go 1.7 将 golang.org/x/net/context 包作为 context 移入标准库。这允许在其他标准库包(包括netnet/httpos/exec)中使用上下文进行取消、超时和传递请求范围的数据,如下所述。

有关上下文的更多信息,请参阅包文档和 Go 博客文章“Go 并发模式:Context”。

HTTP 追踪

Go 1.7 引入了 net/http/httptrace,一个提供追踪 HTTP 请求中事件机制的包。

测试

testing 包现在支持定义带有子测试的测试和带有子基准测试的基准测试。此支持使得编写表格驱动的基准测试和创建分层测试变得容易。它还提供了一种共享通用设置和拆卸代码的方法。有关详细信息,请参阅包文档

运行时

运行时启动的所有 panic 现在都使用实现了内置 errorruntime.Error 的 panic 值,如语言规范所要求

在 panic 期间,如果已知信号名称,它将打印在堆栈追踪中。否则,将使用信号编号,就像 Go1.7 之前一样。

新函数 KeepAlive 提供了一种明确的机制,用于声明一个已分配对象在程序的特定点必须被视为可达,通常是为了延迟相关终结器的执行。

新函数 CallersFrames 将从 Callers 获取的 PC 切片转换为与调用堆栈对应的帧序列。应优先使用此新 API 而不是直接使用 FuncForPC,因为帧序列可以更准确地描述带有内联函数调用的调用堆栈。

新函数 SetCgoTraceback 有助于 Go 和使用 cgo 调用的在同一进程中执行的 C 代码之间更紧密的集成。

在 32 位系统上,运行时现在可以使用操作系统在地址空间中任意位置分配的内存,消除了在某些环境中常见的“OS 分配的内存不在可用范围内”的故障。

运行时现在可以在所有架构上将未使用的内存返回给操作系统。在 Go 1.6 及更早版本中,运行时无法在 ARM64、64 位 PowerPC 或 MIPS 上释放内存。

在 Windows 上,Go 1.5 及更早版本中的 Go 程序通过调用 timeBeginPeriod(1) 在启动时将全局 Windows 计时器分辨率强制设置为 1ms。更改全局计时器分辨率在某些系统上导致问题,并且测试表明不需要此调用以获得良好的调度程序性能,因此 Go 1.6 删除了此调用。Go 1.7 重新引入了此调用:在某些工作负载下,此调用仍然是良好调度程序性能所必需的。

对库的微小更改

与往常一样,库中有各种微小的更改和更新,这些都是在遵守 Go 1 兼容性承诺的前提下进行的。

bufio

在 Go 的早期版本中,如果 ReaderPeek 方法请求的字节数超过底层缓冲区可容纳的字节数,它将返回一个空切片和错误 ErrBufferFull。现在它返回整个底层缓冲区,仍然伴随着错误 ErrBufferFull

bytes

新增了 ContainsAnyContainsRune 函数,以与 strings 包对称。

在 Go 的早期版本中,如果 ReaderRead 方法在没有剩余数据的情况下请求零字节,它将返回计数 0 且没有错误。现在它返回计数 0 和错误 io.EOF

Reader 类型新增了一个方法 Reset,以允许重用 Reader

compress/flate

整个包中有许多性能优化。解压速度提高了约 10%,而 DefaultCompression 的压缩速度是原来的两倍。

除了这些通用改进之外,BestSpeed 压缩器已完全替换,并使用类似于 Snappy 的算法,导致速度提高了约 2.5 倍,尽管输出可能比以前的算法大 5-10%。

还新增了一个压缩级别 HuffmanOnly,它只应用霍夫曼编码而不应用 Lempel-Ziv 编码。放弃 Lempel-Ziv 编码意味着 HuffmanOnly 的运行速度比新的 BestSpeed 快约 3 倍,但代价是生成比新的 BestSpeed 大 20-40% 的压缩输出。

重要的是要注意,BestSpeedHuffmanOnly 都生成符合 RFC 1951 的压缩输出。换句话说,任何有效的 DEFLATE 解压器都将能够继续解压这些输出。

最后,解压器对 io.Reader 的实现有一个细微更改。在以前的版本中,解压器会推迟报告 io.EOF,直到恰好无法读取更多字节。现在,它在读取最后一组字节时更积极地报告 io.EOF

crypto/tls

TLS 实现对每个连接发送的前几个数据包使用较小的记录大小,逐渐增加到 TLS 最大记录大小。这种启发式方法减少了在解密第一个数据包之前必须接收的数据量,从而改善了低带宽网络上的通信延迟。将 ConfigDynamicRecordSizingDisabled 字段设置为 true 会强制 Go 1.6 及更早版本的行为,即从连接开始时数据包就尽可能大。

TLS 客户端现在可选地、有限地支持服务器发起的重新协商,通过设置 ConfigRenegotiation 字段启用。这是连接到许多 Microsoft Azure 服务器所必需的。

包返回的错误现在始终以 tls: 前缀开头。在过去的版本中,一些错误使用 crypto/tls: 前缀,一些使用 tls: 前缀,还有一些完全没有前缀。

生成自签名证书时,包默认不再设置“Authority Key Identifier”字段。

crypto/x509

新函数 SystemCertPool 在可用时提供对整个系统证书池的访问。还有一个新的相关错误类型 SystemRootsError

debug/dwarf

Reader 类型的新方法 SeekPCData 类型的新方法 Ranges 有助于查找要传递给 LineReader 的编译单元,并识别给定程序计数器的特定函数。

debug/elf

新的 R_390 重定位类型及其许多预定义常量支持 S390 端口。

encoding/asn1

ASN.1 解码器现在拒绝非最小整数编码。这可能导致包拒绝一些无效但以前接受的 ASN.1 数据。

encoding/json

Encoder 的新方法 SetIndent 设置 JSON 编码的缩进参数,就像顶层 Indent 函数一样。

Encoder 的新方法 SetEscapeHTML 控制带引号字符串中的 &<> 字符是否应分别转义为 \u0026\u003c\u003e。与以前的版本一样,编码器默认应用此转义,以避免在将 JSON 嵌入 HTML 时可能出现的一些问题。

在 Go 的早期版本中,此包仅支持使用字符串类型键的映射的编码和解码。Go 1.7 增加了对使用整数类型键的映射的支持:编码使用带引号的十进制表示作为 JSON 键。Go 1.7 还增加了对使用实现 MarshalText(参见 encoding.TextMarshaler)方法的非字符串键的映射的编码支持,以及对使用实现 UnmarshalText(参见 encoding.TextUnmarshaler)方法的非字符串键的映射的解码支持。对于字符串类型键,这些方法会被忽略,以保留 Go 早期版本中使用的编码和解码。

编码类型化字节切片时,Marshal 现在会生成一个元素数组,使用该字节类型的 MarshalJSONMarshalText 方法(如果存在)进行编码,仅当两种方法都不可用时才回退到默认的 base64 编码字符串数据。Go 的早期版本接受原始的 base64 编码字符串编码和数组编码(假设字节类型也相应实现了 UnmarshalJSONUnmarshalText),因此此更改在语义上应与 Go 的早期版本向后兼容,尽管它确实更改了所选的编码。

go/build

为了实现 go 命令对仅二进制包和 cgo 包中 Fortran 代码的新支持,Package 类型添加了新的字段 BinaryOnlyCgoFFLAGSFFiles

go/doc

为了支持上面描述的 go test 中的相应更改,Example 结构添加了一个 Unordered 字段,指示示例是否可以按任意顺序生成其输出行。

io

包添加了新的常量 SeekStartSeekCurrentSeekEnd,用于 Seeker 实现。这些常量优于 os.SEEK_SETos.SEEK_CURos.SEEK_END,但后者将为兼容性而保留。

math/big

Float 类型添加了 GobEncodeGobDecode 方法,因此 Float 类型的值现在可以使用 encoding/gob 包进行编码和解码。

math/rand

Read 函数和 RandRead 方法现在生成一个一致且不依赖于输入缓冲区大小的伪随机字节流。

文档澄清了 Rand 的 SeedRead 方法不能并发调用,尽管全局函数 SeedRead 是(并且一直都是)安全的。

mime/multipart

Writer 实现现在按键对每个多部分部分的头部进行排序。以前,迭代映射会导致部分头部的顺序不确定。

net

作为 context 引入的一部分,Dialer 类型新增了一个方法 DialContext,它类似于 Dial,但为拨号操作添加了 context.Context。上下文旨在取代 DialerCancelDeadline 字段,但为了向后兼容性,实现仍然尊重它们。

IP 类型的 String 方法对无效 IP 地址的结果已更改。在过去版本中,如果 IP 字节切片的长度不是 0、4 或 16,String 会返回 "?"。Go 1.7 增加了字节的十六进制编码,例如 "?12ab"

纯 Go 名称解析实现现在尊重 nsswitch.conf 中关于 DNS 查找与本地文件(即 /etc/hosts)查找优先级的声明。

net/http

ResponseWriter 的文档现在明确指出,开始写入响应可能会阻止将来读取请求正文。为了最大限度地兼容,建议实现在写入响应的任何部分之前完整读取请求正文。

作为 context 引入的一部分,Request 增加了新的方法 Context 用于检索相关上下文,以及 WithContext 用于构造一个带有修改上下文的 Request 副本。

Server 实现中,Serve 在请求上下文中记录了底层 *Server(使用键 ServerContextKey)和接收请求的本地地址(一个 Addr)(使用键 LocalAddrContextKey)。例如,接收请求的地址是 req.Context().Value(http.LocalAddrContextKey).(net.Addr)

服务器的 Serve 方法现在仅在 Server.TLSConfig 字段为 nil 或其 TLSConfig.NextProtos 中包含 "h2" 时才启用 HTTP/2 支持。

服务器实现现在按照协议要求将小于 100 的响应码填充为三位数,因此 w.WriteHeader(5) 使用 HTTP 响应状态 005,而不仅仅是 5

服务器实现现在在显式设置“chunked”时,根据 RFC 7230,正确地只发送一个“Transfer-Encoding”头。

服务器实现现在对于拒绝带有无效 HTTP 版本的请求更加严格。声称是 HTTP/0.x 的无效请求现在被拒绝(HTTP/0.9 从未完全支持),并且除“PRI * HTTP/2.0”升级请求之外的明文 HTTP/2 请求现在也被拒绝。服务器继续处理加密的 HTTP/2 请求。

在服务器中,当响应正文为空时,超时处理程序会返回 200 状态码,而不是返回 0 作为状态码。

在客户端中,Transport 实现将请求上下文传递给连接到远程服务器的任何拨号操作。如果需要自定义拨号器,新的 Transport 字段 DialContext 优于现有的 Dial 字段,以便传输可以提供上下文。

Transport 还增加了 IdleConnTimeoutMaxIdleConnsMaxResponseHeaderBytes 字段,以帮助控制空闲或多话服务器消耗的客户端资源。

Client 配置的 CheckRedirect 函数现在可以返回 ErrUseLastResponse,表示应将最近的重定向响应作为 HTTP 请求的结果返回。该响应现在可以通过 req.Response 提供给 CheckRedirect 函数。

自 Go 1 以来,HTTP 客户端的默认行为是使用 Accept-Encoding 请求头请求服务器端压缩,然后透明地解压响应体,此行为可通过 TransportDisableCompression 字段进行调整。在 Go 1.7 中,为了辅助 HTTP 代理的实现,Response 的新字段 Uncompressed 报告是否发生了透明解压。

DetectContentType 增加了对几种新的音频和视频内容类型的支持。

net/http/cgi

Handler 增加了一个新字段 Stderr,允许将子进程的标准错误重定向到主机进程的标准错误之外。

net/http/httptest

新函数 NewRequest 准备一个新的 http.Request,适合在测试期间传递给 http.Handler

ResponseRecorder 的新方法 Result 返回记录的 http.Response。需要检查响应的头部或尾部的测试应调用 Result 并检查响应字段,而不是直接访问 ResponseRecorderHeaderMap

net/http/httputil

ReverseProxy 实现现在在无法连接到后端时返回“502 Bad Gateway”;在早期版本中,它返回“500 Internal Server Error”。

ClientConnServerConn 都已记录为已弃用。它们是低级的、旧的、Go 当前 HTTP 堆栈未使用的,并且将不再更新。程序应改用 http.Clienthttp.Transporthttp.Server

net/http/pprof

运行时追踪 HTTP 处理程序(安装用于处理路径 /debug/pprof/trace)现在在其 seconds 查询参数中接受小数,允许收集小于一秒的追踪。这在繁忙的服务器上特别有用。

net/mail

地址解析器现在允许地址中包含未转义的 UTF-8 文本,遵循 RFC 6532,但它不对结果应用任何规范化。为了与旧的邮件解析器兼容,地址编码器,即 AddressString 方法,继续按照 RFC 5322 转义所有 UTF-8 文本。

ParseAddress 函数和 AddressParser.Parse 方法现在更加严格。它们以前会忽略电子邮件地址后面的任何字符,但现在会返回除空白字符以外的任何内容的错误。

net/url

URL 的新字段 ForceQuery 记录 URL 是否必须包含查询字符串,以区分没有查询字符串的 URL(如 /search)和包含空查询字符串的 URL(如 /search?)。

os

在存在 syscall.ENOTEMPTY 错误的系统上,IsExist 现在对 syscall.ENOTEMPTY 返回 true。

在 Windows 上,Remove 现在在可能的情况下会删除只读文件,使实现行为与非 Windows 系统一致。

os/exec

作为 context 引入的一部分,新的构造函数 CommandContext 类似于 Command,但包含一个可用于取消命令执行的上下文。

os/user

即使 cgo 不可用,Current 函数现在也已实现。

新的 Group 类型,以及查找函数 LookupGroupLookupGroupId,以及 User 结构体中的新字段 GroupIds,提供对系统特定用户组信息的访问。

reflect

尽管 ValueField 方法一直被文档记录为如果给定的字段编号 i 超出范围就会 panic,但它反而默默地返回了一个零 Value。Go 1.7 更改了该方法的行为,使其符合文档。

新的 StructOf 函数在运行时构造结构类型。它完善了类型构造函数集,加入了 ArrayOfChanOfFuncOfMapOfPtrToSliceOf

StructTag 的新方法 Lookup 类似于 Get,但它区分不包含给定键的标签与将空字符串与给定键关联的标签。

TypeValueMethodNumMethod 方法不再返回或计算未导出的方法。

strings

在 Go 的早期版本中,如果 ReaderRead 方法在没有剩余数据的情况下请求零字节,它将返回计数 0 且没有错误。现在它返回计数 0 和错误 io.EOF

Reader 类型新增了一个方法 Reset,以允许重用 Reader

time

Duration 的 time.Duration.String 方法现在将零持续时间报告为 "0s",而不是 "0"ParseDuration 继续接受这两种形式。

方法调用 time.Local.String() 现在在所有系统上都返回 "Local";在早期版本中,它在 Windows 上返回一个空字符串。

$GOROOT/lib/time 中的时区数据库已更新到 IANA 2016d 版本。此备用数据库仅在找不到系统时区数据库时使用,例如在 Windows 上。Windows 时区缩写列表也已更新。

syscall

在 Linux 上,SysProcAttr 结构体(如在 os/exec.CmdSysProcAttr 字段中使用)新增了一个 Unshareflags 字段。如果该字段非零,则由 ForkExec(如在 exec.CmdRun 方法中使用)创建的子进程将在执行新程序之前调用 unshare(2) 系统调用。

unicode

unicode 包和整个系统中的相关支持已从版本 8.0 升级到 Unicode 9.0