Go 1.7 版本说明

Go 1.7 简介

最新的 Go 版本 1.7 在 1.6 发布六个月后发布。它的大多数更改都在工具链、运行时和库的实现中。语言规范只有一个很小的更改。与往常一样,该版本保持了 Go 1 的 兼容性承诺。我们预计几乎所有 Go 程序都将继续像以前一样编译和运行。

该版本 添加了对 IBM LinuxOne 的端口支持更新了 x86-64 编译器后端 以生成更有效的代码;包含了 context 包,它从 x/net 子仓库 中提升而来,现在已在标准库中使用;并 在测试包中添加了对创建测试和基准测试层次结构的支持。该版本还 最终确定了在 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 Systems (linux/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 包含 Go 1.6.1 版本的 gccgo。下一个版本,GCC 7,可能将包含 Go 1.8 版本的 gccgo。

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 中明显更短。

标准库

上下文

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

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

HTTP 跟踪

Go 1.7 引入了 net/http/httptrace,这是一个提供机制用于跟踪 HTTP 请求内部事件的包。

测试

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

运行时

运行时启动的所有恐慌现在都使用实现内置 errorruntime.Error 两种类型的恐慌值,如 语言规范要求

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

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

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

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

在 32 位系统上,运行时现在可以使用操作系统在地址空间中的任何地方分配的内存,从而消除了一些环境中常见的“操作系统分配的内存不在可用范围内”错误。

运行时现在可以在所有架构上将未使用的内存返回给操作系统。在 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 且无错误。现在它返回计数 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: 前缀,而另一些则根本没有前缀。

在生成自签名证书时,包不再默认设置“颁发机构密钥标识符”字段。

crypto/x509

新函数 SystemCertPool 提供了对整个系统证书池的访问权限(如果可用)。还存在一个新的关联错误类型 SystemRootsError

debug/dwarf

Reader 类型的新的 SeekPC 方法和 Data 类型的新的 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 方法,因此现在可以使用 encoding/gob 包对 Float 类型的值进行编码和解码。

math/rand

Read 函数和 RandRead 方法现在会生成一致的伪随机字节流,该字节流与输入缓冲区的大小无关。

文档澄清了 RandSeedRead 方法不能并发调用,虽然全局函数 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 在请求上下文中记录了使用键 ServerContextKey 的基础 *Server 以及使用键 LocalAddrContextKey 接收请求的本地地址(Addr)。例如,接收请求的地址是 req.Context().Value(http.LocalAddrContextKey).(net.Addr)

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

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

服务器实现现在根据 RFC 7230,在显式设置了 “chunked” 时仅发送一个 “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 字段,以帮助控制空闲或健谈服务器消耗的客户端资源。

配置的 ClientCheckRedirect 函数现在可以返回 ErrUseLastResponse 以指示应将最新的重定向响应作为 HTTP 请求的结果返回。该响应现在可作为 req.ResponseCheckRedirect 函数使用。

自 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

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

net/mail

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

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

net/url

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

os

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

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

os/exec

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

os/user

Current 函数现在即使在 cgo 不可用时也能实现。

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

reflect

虽然 ValueField 方法一直都被记录为如果给定的字段编号 i 超出范围就会恐慌,但它实际上是静默地返回了一个零 Value。Go 1.7 将该方法更改为按文档行为。

新的 StructOf 函数在运行时构造一个结构类型。它完成了类型构造器的集合,加入了 ArrayOfChanOfFuncOfMapOfPtrToSliceOf

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

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

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