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 Systems(linux/s390x
)的实验性移植,以及对 Plan 9 on ARM(plan9/arm
)的初步移植。
Go 1.6 中增加的对 64 位 MIPS 上的 Linux(linux/mips64
和 linux/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 系统,向量寄存器名称已更正为 V0
到 V31
;以前的版本错误地将其称为 V32
到 V63
。
对于 64 位 x86 系统,已添加以下指令:PCMPESTRI
、RORXL
、RORXQ
、VINSERTI128
、VPADDD
、VPADDQ
、VPALIGNR
、VPBLENDD
、VPERM2F128
、VPERM2I128
、VPOR
、VPSHUFB
、VPSHUFD
、VPSLLD
、VPSLLDQ
、VPSLLQ
、VPSRLD
、VPSRLDQ
和 VPSRLQ
。
编译器工具链
此版本包含了针对 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.bash
、make.bat
或 make.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
包(参见下文)中的 WithCancel
、WithTimeout
和 WithDeadline
函数返回的取消函数。未能调用该函数会阻止新的 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/sha1
、crypto/sha256
、encoding/binary
、fmt
、hash/adler32
、hash/crc32
、hash/crc64
、image/color
、math/big
、strconv
、strings
、unicode
和 unicode/utf16
包中的实现已进行重大优化,性能提升超过 10%。
对于具有大量空闲 goroutine、堆栈大小大幅波动或大型包级变量的程序,垃圾收集暂停时间应比 Go 1.6 短得多。
标准库
Context
Go 1.7 将 golang.org/x/net/context
包作为 context
移入标准库。这允许在其他标准库包(包括net、net/http 和os/exec)中使用上下文进行取消、超时和传递请求范围的数据,如下所述。
有关上下文的更多信息,请参阅包文档和 Go 博客文章“Go 并发模式:Context”。
HTTP 追踪
Go 1.7 引入了 net/http/httptrace
,一个提供追踪 HTTP 请求中事件机制的包。
测试
testing
包现在支持定义带有子测试的测试和带有子基准测试的基准测试。此支持使得编写表格驱动的基准测试和创建分层测试变得容易。它还提供了一种共享通用设置和拆卸代码的方法。有关详细信息,请参阅包文档。
运行时
运行时启动的所有 panic 现在都使用实现了内置 error
和 runtime.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 的早期版本中,如果 Reader
的 Peek
方法请求的字节数超过底层缓冲区可容纳的字节数,它将返回一个空切片和错误 ErrBufferFull
。现在它返回整个底层缓冲区,仍然伴随着错误 ErrBufferFull
。
bytes
新增了 ContainsAny
和 ContainsRune
函数,以与 strings
包对称。
在 Go 的早期版本中,如果 Reader
的 Read
方法在没有剩余数据的情况下请求零字节,它将返回计数 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% 的压缩输出。
重要的是要注意,BestSpeed
和 HuffmanOnly
都生成符合 RFC 1951 的压缩输出。换句话说,任何有效的 DEFLATE 解压器都将能够继续解压这些输出。
最后,解压器对 io.Reader
的实现有一个细微更改。在以前的版本中,解压器会推迟报告 io.EOF
,直到恰好无法读取更多字节。现在,它在读取最后一组字节时更积极地报告 io.EOF
。
crypto/tls
TLS 实现对每个连接发送的前几个数据包使用较小的记录大小,逐渐增加到 TLS 最大记录大小。这种启发式方法减少了在解密第一个数据包之前必须接收的数据量,从而改善了低带宽网络上的通信延迟。将 Config
的 DynamicRecordSizingDisabled
字段设置为 true 会强制 Go 1.6 及更早版本的行为,即从连接开始时数据包就尽可能大。
TLS 客户端现在可选地、有限地支持服务器发起的重新协商,通过设置 Config
的 Renegotiation
字段启用。这是连接到许多 Microsoft Azure 服务器所必需的。
包返回的错误现在始终以 tls:
前缀开头。在过去的版本中,一些错误使用 crypto/tls:
前缀,一些使用 tls:
前缀,还有一些完全没有前缀。
生成自签名证书时,包默认不再设置“Authority Key Identifier”字段。
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
现在会生成一个元素数组,使用该字节类型的 MarshalJSON
或 MarshalText
方法(如果存在)进行编码,仅当两种方法都不可用时才回退到默认的 base64 编码字符串数据。Go 的早期版本接受原始的 base64 编码字符串编码和数组编码(假设字节类型也相应实现了 UnmarshalJSON
或 UnmarshalText
),因此此更改在语义上应与 Go 的早期版本向后兼容,尽管它确实更改了所选的编码。
go/build
为了实现 go 命令对仅二进制包和 cgo 包中 Fortran 代码的新支持,Package
类型添加了新的字段 BinaryOnly
、CgoFFLAGS
和 FFiles
。
go/doc
为了支持上面描述的 go
test
中的相应更改,Example
结构添加了一个 Unordered 字段,指示示例是否可以按任意顺序生成其输出行。
io
包添加了新的常量 SeekStart
、SeekCurrent
和 SeekEnd
,用于 Seeker
实现。这些常量优于 os.SEEK_SET
、os.SEEK_CUR
和 os.SEEK_END
,但后者将为兼容性而保留。
math/big
Float
类型添加了 GobEncode
和 GobDecode
方法,因此 Float
类型的值现在可以使用 encoding/gob
包进行编码和解码。
math/rand
Read
函数和 Rand
的 Read
方法现在生成一个一致且不依赖于输入缓冲区大小的伪随机字节流。
文档澄清了 Rand 的 Seed
和 Read
方法不能并发调用,尽管全局函数 Seed
和 Read
是(并且一直都是)安全的。
mime/multipart
Writer
实现现在按键对每个多部分部分的头部进行排序。以前,迭代映射会导致部分头部的顺序不确定。
net
作为 context 引入的一部分,Dialer
类型新增了一个方法 DialContext
,它类似于 Dial
,但为拨号操作添加了 context.Context
。上下文旨在取代 Dialer
的 Cancel
和 Deadline
字段,但为了向后兼容性,实现仍然尊重它们。
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
还增加了 IdleConnTimeout
、MaxIdleConns
和 MaxResponseHeaderBytes
字段,以帮助控制空闲或多话服务器消耗的客户端资源。
Client
配置的 CheckRedirect
函数现在可以返回 ErrUseLastResponse
,表示应将最近的重定向响应作为 HTTP 请求的结果返回。该响应现在可以通过 req.Response
提供给 CheckRedirect
函数。
自 Go 1 以来,HTTP 客户端的默认行为是使用 Accept-Encoding
请求头请求服务器端压缩,然后透明地解压响应体,此行为可通过 Transport
的 DisableCompression
字段进行调整。在 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
并检查响应字段,而不是直接访问 ResponseRecorder
的 HeaderMap
。
net/http/httputil
ReverseProxy
实现现在在无法连接到后端时返回“502 Bad Gateway”;在早期版本中,它返回“500 Internal Server Error”。
ClientConn
和 ServerConn
都已记录为已弃用。它们是低级的、旧的、Go 当前 HTTP 堆栈未使用的,并且将不再更新。程序应改用 http.Client
、http.Transport
和 http.Server
。
net/http/pprof
运行时追踪 HTTP 处理程序(安装用于处理路径 /debug/pprof/trace
)现在在其 seconds
查询参数中接受小数,允许收集小于一秒的追踪。这在繁忙的服务器上特别有用。
net/mail
地址解析器现在允许地址中包含未转义的 UTF-8 文本,遵循 RFC 6532,但它不对结果应用任何规范化。为了与旧的邮件解析器兼容,地址编码器,即 Address
的 String
方法,继续按照 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
类型,以及查找函数 LookupGroup
和 LookupGroupId
,以及 User
结构体中的新字段 GroupIds
,提供对系统特定用户组信息的访问。
reflect
尽管 Value
的 Field
方法一直被文档记录为如果给定的字段编号 i
超出范围就会 panic,但它反而默默地返回了一个零 Value
。Go 1.7 更改了该方法的行为,使其符合文档。
新的 StructOf
函数在运行时构造结构类型。它完善了类型构造函数集,加入了 ArrayOf
、ChanOf
、FuncOf
、MapOf
、PtrTo
和 SliceOf
。
StructTag
的新方法 Lookup
类似于 Get
,但它区分不包含给定键的标签与将空字符串与给定键关联的标签。
Type
和 Value
的 Method
和 NumMethod
方法不再返回或计算未导出的方法。
strings
在 Go 的早期版本中,如果 Reader
的 Read
方法在没有剩余数据的情况下请求零字节,它将返回计数 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.Cmd
的 SysProcAttr
字段中使用)新增了一个 Unshareflags
字段。如果该字段非零,则由 ForkExec
(如在 exec.Cmd
的 Run
方法中使用)创建的子进程将在执行新程序之前调用 unshare(2) 系统调用。
unicode
unicode
包和整个系统中的相关支持已从版本 8.0 升级到 Unicode 9.0。