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 现在包含对 DragonFly BSD 在 amd64(64 位 x86)和 386(32 位 x86)架构上的实验性支持。它使用 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

Native Client 虚拟机架构的支持随 Go 1.3 版本回归。它运行在 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 现在包含对 Plan 9 在 386(32 位 x86)架构上的实验性支持。它需要 Tsemacquire 系统调用,该调用自 2012 年 6 月起已包含在 Plan 9 中。

支持 Solaris

Go 1.3 现在包含对 Solaris 在 amd64(64 位 x86)架构上的实验性支持。它需要 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 rungo 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 的变更

cmd/cgo 命令(用于处理 Go 包中的 import "C" 声明)纠正了一个可能导致某些包停止编译的严重错误。以前,所有指向不完整结构体类型的指针都会转换为 Go 类型 *[0]byte,导致 Go 编译器无法诊断将一种结构体指针传递给预期另一种结构体的函数。Go 1.3 通过将每种不同的不完整结构体翻译成不同的命名类型来纠正此错误。

鉴于 C 语言声明 typedef struct S T 用于不完整的 struct S,某些 Go 代码利用此错误互换地引用类型 C.struct_SC.T。Cgo 现在明确允许此用法,即使对于完整的结构体类型也是如此。然而,某些 Go 代码也利用此错误将(例如)一个 *C.FILE 从一个包传递到另一个包。这是不合法的,并且不再奏效:一般来说,Go 包应避免在其 API 中暴露 C 类型和名称。

更新提示:混淆不完整类型指针或将它们跨包边界传递的代码将不再编译,必须重写。如果转换是正确的且必须保留,请使用通过 unsafe.Pointer 的显式转换。

使用 SWIG 的程序需要 SWIG 3.0

对于使用 SWIG 的 Go 程序,现在需要 SWIG 3.0 版本。cmd/go 命令现在将直接把 SWIG 生成的目标文件链接到二进制文件中,而不是构建并链接共享库。

命令行标志解析

在 gc 工具链中,汇编器现在使用与 Go flag 包相同的命令行标志解析规则,这与传统的 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 二进制文件的性能在许多情况下得到了提高。显著的例子包括:

此外,运行时现在在栈转储中包含 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 的修复可能会破坏现有代码,但这些代码是错误的,应该更新。

库的次要变更

以下列表总结了库中的一些次要变更,主要是新增内容。有关每个变更的更多信息,请参阅相关包文档。