Go 1.12

新的Go版本按时在2月25号发布了,主要的变化依旧集中在工具链、运行时和核心库,Release Note:Go 1.12 Release Notes

操作系统与架构

竞态检测器增加对linux/arm64平台的支持。

Go 1.12是最后一个支持FreeBSD 10.x的版本,Go 1.13将要求FreeBSD 11.2以上或FreeBSD 12.0以上版本。对于FreeBSD 12.0以上版本要求内核开启COMPAT_FREEBSD11选项(默认已开启)。

cgo增加对linux/ppc64平台的支持。

GOOS环境变量可识别hurd,为gccgo预留对GNU/Hurd系统的支持。

Windows

新的windows/arm移植支持在搭载Windows 10 IoT Core的32位ARM芯片(例如树莓派3)上运行Go。

AIX

现已支持POWER8架构(aix/ppc64)上的AIX 7.2及后续版本,但还不支持外部链接、cgo、pprof及静态检测器。

Darwin

Go 1.12是最后一个支持maOS 10.10的版本,Go 1.13将要求macOS 10.11以上版本。

Darwin上的系统调用都会通过libSystem执行,以确保支持后续的macOS及iOS版本。切换到libSystem可能触发App Store对使用私有API的检查,因为libSystem被认为是私有的库,syscall.Getdirentries调用在iOS上将会一直以ENOSYS错误失败返回。

工具

停止对go tool vet的支持

go vet命令已重写以服务许多不同的源码分析工具,参考golang.org/x/tools/go/analysis包获取详情。带来的负面影响是不再支持go tool vet。使用go tool vet的外部工具需改为使用go vet。使用go vet代替go tool vet应该可以在所有受支持的Go版本上正常运行。

作为这个变化的一部分,试验性的 -shadow 选项在go vet中不再支持。可以使用下面的方式检查变量隐藏。

1
2
go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

Tour

Go Tour不再包含在主要的二进制发行版中。若需要在本地运行,需要手动安装。

1
2
go get -u golang.org/x/tour
tour

Build cache要求

作为消除 $GOPATH/pkg 的一步,现在必须启用build cache用。设置环境变量GOCACHE=off会导致go命令写缓存失败。

二进制包

Go 1.12是最后一个支持二进制包的版本。

Cgo

Go 1.12会将C类型EGLDisplay翻译为Go类型unitptr。这个变化类似Go 1.10及以后版本处理Darwin的CoreFoundation和Java的JNI类型的方式。参考cgo文档获取更多信息。

使用Cgo的软件包不再接受Mangled C名称。例如,使用记录的cgo名称C.char而不是cgo生成的错位名称_Ctype_char。

模块

GO111MODULE设置为on时,go命令现在支持模块目录之外的模块感知操作,前提是这些操作不需要解析相对于当前目录的导入路径或显式编辑go.mod文件。诸如go get,go list和go mod download之类的命令就像在具有初始空要求的模块中一样。在这种模式下,go env GOMOD返回系统的null设备(/dev/null或NUL)。

下载和解压缩模块的go命令现在可以安全地并发调用。模块缓存($GOPATH/pkg/mod)必须驻留在支持文件锁定的文件系统中。

go.mod文件中的go指令现在会包含该模块中文件使用的Go语言版本。若未设置版本,它将被设置为当前版本(Go 1.12)。如果模块go指令指定的版本比正在使用的工具链更高,go命令将尝试构建软件包,并且仅在该构建失败时才会提示不匹配。

这个go指令使用的变化意味着如果你使用Go 1.12来构建一个模块,将在go.mod文件中记录为1.12,那么在尝试使用Go 1.11到Go 1.11.3构建相同模块时会出错。Go 1.11.4或更高版本可以正常运行。如果你必须使用Go 1.11到1.11.3,你可以使用Go 1.12 go工具,通过go mod edit -go=1.11将语言版本设置为1.11来避免此问题。

当使用活动模块无法解析导入时,go命令在查询模块缓存或网络资源前,会首先尝试使用主模块中的replace指令指定的模块。如果找到匹配的替换但replace指令未指定版本时,go命令使用time.Time零值(例如v0.0.0-00010101000000-000000000000)派生的伪版本。

编译器工具链

编译器的实时变量分析已得到改进。这可能意味着当前版本中的终结器将比以前的执行更快。如果引起问题,可以考虑适当添加runtime.KeepAlive调用。

更多的函数可以默认进行内联,包括那些除了调用其他函数外不执行任何操作的函数。这种额外的内联令使用runtime.CallersFrames代替直接迭代runtime.Callers的结果变得更加重要。

1
2
3
4
5
6
7
8
9
// 不正常工作的旧代码(它会忽略内联调用)。
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
for _, pc := range pcs[:n] {
	f := runtime.FuncForPC(pc)
	if f != nil {
		fmt.Println(f.Name())
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 正常工作的新代码
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
for {
	frame, more := frames.Next()
	fmt.Println(frame.Function)
	if !more {
		break
	}
}

runtime.CallersFramesruntime.Stack不再报告编译器生成的用于实现方法表达式的wrapper。它们也不会在panic堆栈追踪中打印出来。这个修改使gc工具链与gccgo工具链保持一致,后者已经从堆栈追踪中删除了此类wrapper。这些API的使用者应该需要为丢失的帧做调整,对于必须在1.11和1.12版本之间进行互操作的代码,需要替换方法表达式x.M为函数表达式func (…) { x.M(…) }

编译器现在支持通过 -lang 标志位设置使用的Go语言版本。例如,设置了 -lang=1.8 后,若程序使用了Go 1.9中引入的type alias,编译器将会报错。Go 1.12前的语言变更不会被强制执行。

编译器工具链现在使用不同的约定来调用Go函数和汇编函数。这对用户来说应该是透明的,除了同时在Go和汇编之间交叉并跨越包边界的调用。如果链接错误类似”relocation target not defined for ABIInternal (but is defined for ABI0)“,请参阅ABI设计文档中的compatibility section

编译器生成的DWARF调试信息也有很多改进,包括参数打印和变量位置信息。

现在Go程序也会在linux/arm64平台上维护堆栈帧指针,以便利用perf等分析工具。维护帧指针的运行时开销很小,平均值大约为3%。如果要构建不使用帧指针的工具链,请在运行make.bash时设置GOEXPERIMENT=noframepointer

过时的”safe“编译器模式(由 -u gcflag 标志位启用)已删除。

godoc及go doc

Go 1.12中godoc只是一个web服务器而不再是一个命令行接口。用户应当使用go doc命令获取命令行帮助输出来替代它。Go 1.12是最后一包含godocweb服务器的版本,在Go 1.13中可以使用go get命令安装它。

现在go doc支持 -all 标志位,和之前的godoc命令一样,它会打印所有导出的API及它们的文档说明。

go doc现在也支持 -src 标志位,用于展示源码。

追踪

tarce工具现在支持绘制mutator利用率曲线,包括对执行追踪的交叉引用。这些对于分析垃圾收集器对应用程序延迟和吞吐量对影响非常有用。

汇编

在arm64架构上,平台寄存器由R18重命名为R18_PLATFORM以防止意外使用,因为操作系统可能会选择保留该寄存器。

运行时

Go 1.12显著改善了在大量堆存内存存活时的扫描性能。这减少了垃圾回收后立刻分配内存的延迟。

Go运行时现在采取更积极的策略将内存释放归还给操作系统,特别是无法重用当前堆空间的大量内存分配。

Go运行时的timer和deadline代码现在运行的更快,且在多核处理器环境下有更好的伸缩性,特别改善了处理网络连接超时的性能。

在Linux平台,运行时现在使用MADV_FREE来释放未使用的内存。这会更高效,但也可能导致更高的RSS报告。内核会在需要的时候重新分配未使用的内存。若需要回滚到Go 1.11的模式(MADV_DONTNEED),可以设置环境变量GODEBUG=madvdontneed=1

现在给GODEBUG环境变量添加cpu.extension=off标志会禁用标准库和运行时中可选的CPU指令集拓展,目前暂不支持Windows平台

Go 1.12通过修复大堆分配的过度计数改善了内存分析的精确性。

回溯,run.Caller及runtime.Callers不再包含编译器生产的初始函数。现在在全局变量初始化期间执行回溯时,将显示名为PKG.init.ializers的函数。

核心库

TLS 1.3

Go 1.12在crypto/tls包中增加来可选的TLS 1.3支持(参考RFC 8446)。可以通过在GODEBUG环境变量中追加tls13=1来启用。Go 1.13中将默认启用TLS 1.3。

如果要执行TLS 1.3握手,请确保未在Config中显式设置MaxVersion并使用环境变量GODEBUG=tls13=1运行程序。

除了ConnectionState和重新握手中的TLSUnique以外,所有的TLS 1.2特性都可以在TLS 1.3中使用,并且提供同等或者更好的安全性和性能。需要注意的是,尽管TLS 1.3向前兼容,但是一些特定版本的老系统可能无法正常握手。RSA证书密钥太短(包括512位密钥)时将无法运行TLS 1.3。

TLS 1.3的加密套件无法配置。所有支持的加密套件都是安全的,若Config中的设置了PreferServerCipherSuites,那么将基于可用的硬件加速来确定首选加密套件。

目前客户端或服务器都不支持Early data(也称为“0-RTT模式”)。另外Go 1.12服务器不支持跳过客户端发送的Early data。 由于TLS 1.3 0-RTT模式涉及客户端需要记录哪些服务器支持0-RTT的状态,当部分其他服务器支持0-RTT时,Go 1.12服务器无法作为负载均衡池的一部分。如果将域名从一个支持0-RTT的服务器切换到一个GO 1.12服务器,那么在切换前,所有签发的会话凭据在生命周期内,都需要禁用0-RTT以确保服务不间断。

在TLS 1.3中客户端是最后一个响应握手的,因此服务端若出现错误,客户端上会在第一个Read中接收到错误而不是通过Handshake,例如服务器拒绝了客户端证书。同样,现在会话凭据是握手后才能接收的消息,因此仅在客户端第一次Read时才接收到。

其他轻微修改

小修小改:Minor changes to the library