Go 1.3 发布说明
Go 1.3 简介
最新发布的 Go 1.3 版在 1.2 版发布六个月后问世,不包含任何语言更改。它主要侧重于实现工作,提供了精确的垃圾回收、对编译器工具链进行重大重构以实现更快的构建(特别是对于大型项目)、全面的显著性能改进以及对 DragonFly BSD、Solaris、Plan 9 和 Google Native Client 架构 (NaCl) 的支持。它还在内存模型方面对同步进行了重要改进。Go 1.3 一如既往地遵守了兼容性承诺,几乎所有内容在迁移到 1.3 后都能继续编译和运行,无需更改。
对支持的操作系统和架构的更改
移除对 Windows 2000 的支持
Microsoft 已于 2010 年停止支持 Windows 2000。由于它在异常处理(在 Unix 术语中称为信号)方面存在实现困难,因此从 Go 1.3 开始,Go 也不再支持它。
支持 DragonFly BSD
Go 1.3 现在包含对 amd64
(64 位 x86)和 386
(32 位 x86)架构上 DragonFly BSD 的实验性支持。它需要 DragonFly BSD 3.6 或更高版本。
支持 FreeBSD
当时未宣布,但自 Go 1.2 发布以来,Go 在 FreeBSD 上的支持需要 FreeBSD 8 或更高版本。
从 Go 1.3 开始,Go 在 FreeBSD 上的支持要求内核使用 COMPAT_FREEBSD32
标志进行编译。
与 ARM 平台改用 EABI 系统调用相结合,Go 1.3 将仅在 FreeBSD 10 上运行。x86 平台 386 和 amd64 不受影响。
支持 Native Client
随着 1.3 版本的发布,Go 重新支持 Native Client 虚拟机架构。它在 32 位 Intel 架构 (GOARCH=386
) 和 64 位 Intel 架构上运行,但使用 32 位指针 (GOARCH=amd64p32
)。目前尚不支持 ARM 上的 Native Client。请注意,这是 Native Client (NaCl),而不是 Portable Native Client (PNaCl)。有关 Native Client 的详细信息此处;如何设置 Go 版本此处有说明。
支持 NetBSD
从 Go 1.3 开始,Go 在 NetBSD 上的支持需要 NetBSD 6.0 或更高版本。
支持 OpenBSD
从 Go 1.3 开始,Go 在 OpenBSD 上的支持需要 OpenBSD 5.5 或更高版本。
支持 Plan 9
Go 1.3 现在包含对 386
(32 位 x86)架构上 Plan 9 的实验性支持。它需要 Tsemacquire
系统调用,该系统调用自 2012 年 6 月以来已在 Plan 9 中提供。
支持 Solaris
Go 1.3 现在包含对 amd64
(64 位 x86)架构上 Solaris 的实验性支持。它需要 illumos、Solaris 11 或更高版本。
内存模型变更
Go 1.3 内存模型添加了一条新规则,涉及对带缓冲通道的发送和接收操作,以明确带缓冲通道可以用作简单的信号量,使用向通道发送来获取,从通道接收来释放。这不是语言更改,只是对通信的预期属性的澄清。
实现和工具的更改
栈
Go 1.3 将 goroutine 栈的实现从旧的“分段”模型更改为连续模型。当 goroutine 需要的栈空间超出可用空间时,其栈会被转移到更大的单个内存块中。这种转移操作的开销分摊得很好,并消除了计算反复跨越段边界时旧的“热点”问题。包括性能数据在内的详细信息请参见此设计文档。
垃圾回收器变更
一段时间以来,垃圾回收器在检查堆中的值时一直是“精确的”;Go 1.3 版本为栈上的值添加了等效的精确度。这意味着非指针 Go 值(例如整数)永远不会被误认为是指针,从而防止未使用的内存被回收。
从 Go 1.3 开始,运行时假定指针类型的值包含指针,而其他值不包含。此假设是栈扩展和垃圾回收的精确行为的基础。使用unsafe
包将整数存储在指针类型值中的程序是非法的,如果运行时检测到此行为,将会崩溃。使用unsafe
包将指针存储在整数类型值中的程序也是非法的,但在执行过程中更难诊断。由于指针对运行时是隐藏的,栈扩展或垃圾回收可能会回收它们指向的内存,从而产生悬空指针。
更新:使用 unsafe.Pointer
将内存中保存的整数类型值转换为指针的代码是非法的,必须重写。此类代码可以通过 go vet
进行识别。
Map 迭代
对小型 map 的迭代不再以一致的顺序进行。Go 1 定义“map 的迭代顺序未指定,并且不保证每次迭代都相同。”为了防止代码依赖 map 迭代顺序,Go 1.0 在 map 的随机索引处开始每次 map 迭代。Go 1.1 中引入的新 map 实现忽略了对包含八个或更少条目的 map 进行随机迭代,尽管迭代顺序仍然会因系统而异。这使得人们可以编写依赖于小型 map 迭代顺序的 Go 1.1 和 Go 1.2 程序,因此只能在某些系统上可靠地工作。Go 1.3 重新引入了对小型 map 的随机迭代,以清除这些错误。
更新:如果代码假定小型 map 的迭代顺序固定,它将中断并且必须重写,以避免做出该假设。由于只影响小型 map,因此问题最常出现在测试中。
连接器
作为 Go 连接器总体大修的一部分,编译器和连接器已经过重构。连接器仍然是一个 C 程序,但现在作为连接器一部分的指令选择阶段已经通过创建名为 liblink
的新库转移到编译器中。通过在首次编译包时只执行一次指令选择,这可以显著加快大型项目的编译速度。
更新:尽管这是一项重大的内部更改,但它应该对程序没有影响。
gccgo 的状态
GCC 4.9 版本将包含 Go 1.2 (而非 1.3) 版本的 gccgo。GCC 和 Go 项目的发布计划不一致,这意味着 1.3 版本将在开发分支中可用,但下一个 GCC 版本 4.10 很可能包含 Go 1.4 版本的 gccgo。
Go 命令的更改
cmd/go
命令有几个新特性。go run
和 go test
子命令支持一个新的 -exec
选项,用于指定运行生成二进制文件的替代方式。其直接目的是支持 NaCl。
当启用竞争检测器时,go test
子命令的测试覆盖率支持现在会自动将覆盖率模式设置为 -atomic
,以消除关于对覆盖率计数器不安全访问的错误报告。
即使包中没有测试文件,go test
子命令现在也总是构建包。以前,如果不存在测试文件,它将不执行任何操作。
go build
子命令支持一个新的 -i
选项,用于安装指定目标的依赖项,但不安装目标本身。
现在支持启用 cgo
进行交叉编译。运行 all.bash 时,CC_FOR_TARGET 和 CXX_FOR_TARGET 环境变量分别用于指定 C 和 C++ 代码的交叉编译器。
最后,go 命令现在支持通过 cgo 导入 Objective-C 文件(后缀为 .m
)的包。
cgo 变更
处理 Go 包中 import "C"
声明的 cmd/cgo
命令已纠正了一个可能导致某些包停止编译的严重错误。以前,所有指向不完整结构类型的指针都转换为 Go 类型 *[0]byte
,导致 Go 编译器无法诊断将一种结构指针传递给预期另一种的函数。Go 1.3 通过将每个不同的不完整结构转换为不同的命名类型来纠正此错误。
给定 C 声明 typedef struct S T
,用于不完整的 struct S
,一些 Go 代码曾利用此 bug 互换地引用 C.struct_S
和 C.T
类型。Cgo 现在明确允许这种用法,即使对于已完成的结构类型也是如此。然而,一些 Go 代码也利用此 bug 将(例如)*C.FILE
从一个包传递到另一个包。这是不合法的,并且不再起作用:通常 Go 包应避免在其 API 中暴露 C 类型和名称。
更新:混淆不完整类型指针或跨包边界传递它们的代码将不再编译,必须重写。如果转换是正确的且必须保留,请使用通过unsafe.Pointer
进行的显式转换。
使用 SWIG 的程序需要 SWIG 3.0
对于使用 SWIG 的 Go 程序,现在需要 SWIG 3.0 版。cmd/go
命令现在将直接将 SWIG 生成的对象文件链接到二进制文件中,而不是构建并链接共享库。
命令行标志解析
在 gc 工具链中,汇编器现在使用与 Go 标志包相同的命令行标志解析规则,这与传统的 Unix 标志解析有所不同。这可能会影响直接调用该工具的脚本。例如,go tool 6a -SDfoo
现在必须写成 go tool 6a -S -D foo
。(在 Go 1.1 中,编译器和链接器也进行了相同的更改。)
godoc 变更
当使用 -analysis
标志调用时,godoc 现在对其索引的代码执行复杂的静态分析。分析结果以源代码视图和包文档视图呈现,包括每个包的调用图以及定义与引用、类型及其方法、接口及其实现、通道上的发送和接收操作、函数及其调用者以及调用站点与其被调用者之间的关系。
杂项
用于比较基准测试运行性能的程序 misc/benchcmp
已重写。它曾是主仓库中的一个 shell 和 awk 脚本,现在是 go.tools
仓库中的一个 Go 程序。文档在此。
对于我们这些构建 Go 发行版的人来说,工具 misc/dist
已被移动并重命名;它现在位于主仓库中的 misc/makerelease
。
性能
由于运行时和垃圾回收的更改,以及一些库的更改,此版本 Go 二进制文件的性能在许多情况下得到了改进。显著的例子包括
- 运行时更有效地处理 defer,使每个调用 defer 的 goroutine 的内存占用减少约两千字节。
- 垃圾回收器已经提速,采用了并发清理算法、更好的并行化和更大的页面。累积效应可以使收集器暂停时间减少 50-70%。
- 竞争检测器(参见本指南)现在速度提升了约 40%。
- 由于实现了第二个一次性执行引擎,正则表达式包
regexp
现在对于某些简单表达式显著加快。使用哪个引擎是自动选择的;细节对用户隐藏。
此外,运行时现在在栈转储中包含 goroutine 被阻塞的时长,这在调试死锁或性能问题时可能很有用。
标准库的更改
新包
标准库中新增了一个包debug/plan9obj
。它实现了对 Plan 9 a.out 目标文件的访问。
库的主要更改
crypto/tls
之前的一个 bug 使得有可能无意中跳过 TLS 中的验证。在 Go 1.3 中,该 bug 已修复:必须指定 ServerName 或 InsecureSkipVerify,如果指定了 ServerName 则强制执行。这可能会破坏现有代码,这些代码错误地依赖于不安全的行为。
标准库中添加了一个重要的新类型:sync.Pool
。它提供了一种高效的机制,用于实现某些类型的缓存,其内存可以由系统自动回收。
testing
包的基准测试辅助函数 B
现在有一个 RunParallel
方法,使得运行利用多 CPU 的基准测试更加容易。
更新:crypto/tls 修复可能会破坏现有代码,但此类代码是错误的,应进行更新。
对库的微小更改
以下列表总结了库的一些次要更改,主要是新增功能。有关每个更改的更多信息,请参阅相关包文档。
- 在
crypto/tls
包中,新增了一个DialWithDialer
函数,允许使用现有拨号器建立 TLS 连接,从而更容易控制拨号选项(如超时)。该包现在还在ConnectionState
结构体中报告连接使用的 TLS 版本。 crypto/tls
包的CreateCertificate
函数现在支持解析(并在其他地方支持序列化)PKCS #10 证书签名请求。fmt
包的格式化打印函数现在定义%F
为打印浮点值时%f
的同义词。math/big
包的Int
和Rat
类型现在实现了encoding.TextMarshaler
和encoding.TextUnmarshaler
。- 复数幂函数
Pow
现在指定了第一个参数为零时的行为。以前未定义。详细信息请参见该函数的文档。 net/http
包现在在新的Response.TLS
字段中公开了用于发出客户端请求的 TLS 连接的属性。net/http
包现在允许使用Server.ErrorLog
设置一个可选的服务器错误日志记录器。默认情况下,所有错误仍然输出到 stderr。net/http
包现在支持通过Server.SetKeepAlivesEnabled
禁用服务器上的 HTTP Keep-Alive 连接。默认情况下,服务器继续保持 Keep-Alive(为多个请求重用连接)。只有资源受限的服务器或处于优雅关闭过程中的服务器才会希望禁用它们。net/http
包添加了一个可选的Transport.TLSHandshakeTimeout
设置,以限制 HTTP 客户端请求等待 TLS 握手完成的时间。现在,它在DefaultTransport
上也默认设置。net/http
包的DefaultTransport
(由 HTTP 客户端代码使用)现在默认启用TCP Keep-Alives。其他Transport
值(其Dial
字段为 nil)继续与以前一样工作:不使用 TCP Keep-Alives。- 当使用
ListenAndServe
或ListenAndServeTLS
时,net/http
包现在为传入的服务器请求启用TCP Keep-Alives。否则启动服务器时,不启用 TCP Keep-Alives。 net/http
包现在提供一个可选的Server.ConnState
回调,用于挂钩服务器连接生命周期的各个阶段(参见ConnState
)。这可用于实现限速或优雅关闭。net/http
包的 HTTP 客户端现在有一个可选的Client.Timeout
字段,用于指定使用客户端发出的请求的端到端超时。net/http
包的Request.ParseMultipartForm
方法现在将在正文的Content-Type
不是multipart/form-data
时返回错误。在 Go 1.3 之前,它会静默失败并返回nil
。依赖于先前行为的代码应该进行更新。- 在
net
包中,Dialer
结构体现在有一个KeepAlive
选项,用于指定连接的 Keep-Alive 周期。 net/http
包的Transport
现在即使在出错时也会一致地关闭Request.Body
。os/exec
包现在实现了文档中始终提到的关于二进制文件相对路径的内容。特别是,它仅在二进制文件的文件名不包含路径分隔符时才调用LookPath
。reflect
包中的SetMapIndex
函数在从nil
map 中删除时不再 panic。- 如果主 goroutine 调用
runtime.Goexit
并且所有其他 goroutine 完成执行,程序现在总是崩溃,报告检测到死锁。Go 的早期版本对这种情况的处理不一致:大多数情况下报告为死锁,但一些简单的情况则干净地退出。 - runtime/debug 包现在有一个新函数
debug.WriteHeapDump
,用于写入堆的描述。 strconv
包中的CanBackquote
函数现在将DEL
字符U+007F
视为不可打印字符。syscall
包现在提供SendmsgN
作为Sendmsg
的替代版本,它返回写入的字节数。- 在 Windows 上,
syscall
包现在通过添加新函数NewCallbackCDecl
以及现有函数NewCallback
来支持 cdecl 调用约定。 testing
包现在诊断调用panic(nil)
的测试,这些测试几乎总是有错误的。此外,测试现在即使在失败时也会写入配置文件(如果使用分析标志调用)。unicode
包和整个系统中的相关支持已从 Unicode 6.2.0 升级到 Unicode 6.3.0。