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 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 的变更
cmd/cgo
命令(用于处理 Go 包中的 import "C"
声明)纠正了一个可能导致某些包停止编译的严重错误。以前,所有指向不完整结构体类型的指针都会转换为 Go 类型 *[0]byte
,导致 Go 编译器无法诊断将一种结构体指针传递给预期另一种结构体的函数。Go 1.3 通过将每种不同的不完整结构体翻译成不同的命名类型来纠正此错误。
鉴于 C 语言声明 typedef struct S T
用于不完整的 struct S
,某些 Go 代码利用此错误互换地引用类型 C.struct_S
和 C.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 二进制文件的性能在许多情况下得到了提高。显著的例子包括:
- 运行时更高效地处理 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。其他Dial
字段为 nil 的Transport
值功能与之前相同:不使用 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
。SetMapIndex
函数在reflect
包中在从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)
的测试,这几乎总是错误的。此外,现在即使在失败时,测试也会写入 profile(如果使用 profiling 标志调用)。unicode
包以及系统中的相关支持已从 Unicode 6.2.0 升级到 Unicode 6.3.0。