Go 1.3 发布说明

Go 1.3 简介

最新的 Go 版本 1.3 在 1.2 发布六个月后发布,并且没有包含任何语言更改。它主要侧重于实现工作,提供了精确的垃圾回收,对编译器工具链进行了重大重构,从而使构建速度更快,尤其是在大型项目中,总体性能显著提升,并支持 DragonFly BSD、Solaris、Plan 9 和 Google 的原生客户端架构 (NaCl)。它还对内存模型进行了一项重要的改进,涉及同步问题。与往常一样,Go 1.3 保持了兼容性承诺,并且几乎所有内容在迁移到 1.3 后都将继续编译和运行而无需更改。

对支持的操作系统和架构的更改

删除对 Windows 2000 的支持

微软于 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 发布以来,在 FreeBSD 上使用 Go 需要 FreeBSD 8 或更高版本。

从 Go 1.3 开始,在 FreeBSD 上使用 Go 需要内核在编译时配置 COMPAT_FREEBSD32 标志。

与切换到 ARM 平台的 EABI 系统调用相一致,Go 1.3 将只在 FreeBSD 10 上运行。x86 平台(386 和 amd64)不受影响。

支持原生客户端

对原生客户端虚拟机架构的支持已在 Go 1.3 版本中恢复。它在 32 位 Intel 架构 (GOARCH=386) 上运行,也运行在 64 位 Intel 上,但使用 32 位指针 (GOARCH=amd64p32)。ARM 上尚不支持原生客户端。请注意,这是原生客户端 (NaCl),而不是可移植原生客户端 (PNaCl)。有关原生客户端的详细信息,请访问此处;有关如何设置 Go 版本的说明,请访问此处

支持 NetBSD

从 Go 1.3 开始,在 NetBSD 上使用 Go 需要 NetBSD 6.0 或更高版本。

支持 OpenBSD

从 Go 1.3 开始,在 OpenBSD 上使用 Go 需要 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 识别。

映射迭代

对小型映射的迭代不再以一致的顺序发生。Go 1 定义“映射上的迭代顺序未指定,并且不保证在每次迭代中都相同。”为了防止代码依赖于映射迭代顺序,Go 1.0 在映射中的随机索引处启动每个映射迭代。Go 1.1 中引入的一个新的映射实现忽略了对具有八个或更少条目的映射进行迭代随机化,尽管迭代顺序仍然可能因系统而异。这使得人们能够编写依赖于小型映射迭代顺序的 Go 1.1 和 Go 1.2 程序,因此只能在某些系统上可靠地工作。Go 1.3 重新引入了对小型映射的随机迭代,以消除这些错误。

更新:如果代码假设小型映射的固定迭代顺序,它将中断并且必须重写,不要进行该假设。由于只有小型映射受到影响,因此问题最常出现在测试中。

作为对 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 中之前的一个错误使得在 TLS 中无意中跳过验证成为可能。在 Go 1.3 中,此错误已修复:必须指定 ServerName 或 InsecureSkipVerify,如果指定了 ServerName,则会强制执行。这可能会破坏依赖于不安全行为的现有代码。

标准库中添加了一个重要的新的类型:sync.Pool。它提供了一种有效的机制来实现某些类型的缓存,这些缓存的内存可以由系统自动回收。

testing 包的基准测试辅助函数B 现在有一个RunParallel 方法,以便更容易运行利用多个 CPU 的基准测试。

更新crypto/tls 修复可能会破坏现有代码,但此类代码有误,应更新。

库的次要更改

以下列表总结了对库的一些次要更改,主要是添加。有关每个更改的更多信息,请参阅相关的包文档。