Go 1.14

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

主要变更如下:

  • 模块支持现在已经可应用于生产环境了
  • 允许嵌入方法集重叠的接口
  • defer性能改善
  • goroutine异步抢占
  • 页分配器效率提升
  • 内部定时器效率提升

其中goroutine异步抢占和定时器优化是我这次关注的重点,我们的应用性能获得了肉眼可见巨大提升,1.14值得升级。

操作系统与架构

Darwin

Go 1.14是最后一个支持maOS 10.11的版本,Go 1.15将要求macOS 10.12以上版本。

Go 1.14将是最后一个支持32位架构的版本,后续的macOS、iOS、iPadOS、watchOS及tvOS都不会支持32位应用了。

Windows

Windows上的Go二进制文件现在已启用DEP(Data Execution Prevention)

在Windows上, 如果未在权限参数中设置0o200(所有者写权限),则通过os.OpenFile(带有os.O_CREATE标志)或syscall.Open(带有syscall.O_CREAT标志)创建文件时,将以只读方式创建。这使得Windows上的行为更像Unix系统。

WebAssembly

Go中使用js.Value引用的JavaScript值现在可以被垃圾回收。

js.Value值不再使用==运算符进行比较,必须使它的Equal方法进行比较。

js.Value现在有IsUndefined,IsNull和IsNaN方法。

RISC-V

Go 1.14包含了对64位RISC-V(GOOS=linux, GOARCH=riscv64)的实验性支持,需要注意,性能,汇编语法的稳定性以及正确性可能还在完善中。

RISC-V

Go现在在FreeBSD 12.0或更高版本(freebsd/arm64)上支持64位ARM体系结构 。

Native Client(NaCl)

如Go 1.13的Release Note提到,Go 1.14移除了对Native Client(GOOS=nacl)平台的支持。

Illumos

现在,运行时会采用的区域CPU上限(zone.cpu-cap资源控件)作为runtime.NumCPU和的GOMAXPROCS的默认值。

工具

Vendoring

当主模块包含顶层vendor目录并且其go.mod文件指定go 1.14或更高版本时,go命令现在默认使用 -mod=vendor 标志。若不存在vendor目录或使用 -mod=mod 标志,go命令将从模块缓存中加载模块。

当设置 -mod=vendor 标志后,go命令现在会验证主模块的 vendor/modules.txt 文件是否与 go.mod 文件一致。

go list -m 不再静默忽略vendor目录中未提供包的可传递依赖项。若设置了 -mod=vendor,并且请求 vendor/modules.txt 中未声明的模块信息时,将显式失败。

Flags

go get 命令不再接受 -mod 标志。在之前,该标志的设置要么被忽略,要么导致构建失败

go.mod 文件为只读且不存在顶层vendor目录时,默认情况下将设置 -mod=readonly

新标志 -modcacherw 指示go命令以默认权限保留模块缓存中的新创建目录,而不是将其设为只读。使用此标志会使测试或其他工具可能意外添加未经模块校验的文件。但是它允许使用rm -rf (而不是go clean -modcache)来删除模块缓存。

新标志 -modfile=file 指示go命令读取(并可能写入)指定go.mod文件,而不是模块根目录中的默认文件。为了确定模块的根目录,仍必须存在一个名为go.mod的文件,但它不能被直接访问。当指定 -modfile 时,也会生成一个备用go.sum文件,它与go.mod文件存储在同一目录下。

环境变量

新的环境变量 GOINSECURE 指示go命令不需要HTTPS连接并跳过证书验证,直接从模块源中获取某些模块。像现有 GOPRIVATE 变量一样,GOINSECURE 是逗号分隔的glob模式列表。

模块外部命令

当显式启用模块感知模式(通过设置 GO111MODULE=on )时,如果不存在go.mod文件,则大多数模块命令的功能将受到更多限制。例如,go build, go run,和其他构建命令,只能使用标准库或命令行go文件的中包进行构建。

在以前,go命令会将每个包路径解析为模块的最新版本,但不会记录模块路径或版本。这导致构建缓慢,不可复制

go get 功能保持不变, go mod download 和指定明确版本的 go list -m 也一样。

+incompatible版本

如果模块的最新版本包含一个go.mod文件,除非显示声明或已经要求使用该版本,go get则将不再升级到该模块的incompatible主版本。 当直接从版本控制中获取模块时,go list 也会忽略该模块的incompatible主版本,但使用代理时,可能包含incompatible主版本。

go.mod文件维护

go mod tidy 以外的go命令不会移除此类require指令:该指令指定了一个间接依赖版本,且该版本已由主模块的其他(传递)依赖项隐含。

如果是不涉及版本的文本变动,go mod tidy 以外的go命令不会再编辑go.mod。

当设置 -mod=readonly 时,go命令不会再因为缺少go指令或错误的 // indirect 注释而失败。

模块下载

go命令现在支持模块模式下的Subversion存储库。

go命令现在包含来自模块代理和其他HTTP服务器的纯文本错误消息的摘要。如果错误消息是有效的UTF-8格式,并且仅包含图形字符和空格,才会被显示出来。

测试

go test -v现在流式传输t.Log输出,之前是在所有测试结束时才输出日志。

运行时

与直接调用延迟函数相比, 此版本提高了大多数场景下使用defer的性能,使defer函数的开销几乎为零。因此,defer现在可以在对性能有要求代码中使用,且无需担心额外开销。

goroutine现在支持异步抢占。因此,没有函数调用的死循环不再会使调度程序死锁或显着延迟垃圾回收。除windows/arm, darwin/arm,js/wasm,和 plan9/*以外的平台都支持该特性。

实现goroutine异步抢占后,包括Linux和macOS系统在内的Unix系统上,使用Go 1.14构建的程序将比使用早期版本构建的程序接收到更多的信号。这意味着使用类似syscallgolang.org/x/sys/unix时,会遇到更多慢系统调用失败,并显示EINTR错误。这些程序必须以某种方式处理这些错误,例如循环执行系统调用。若要获取更多信息,Linux下请参考man 7 signal,其他系统请参考类似的文档。

页分配器的效率提升,并且在GOMAXPROCS数值较高时不易产生大量锁竞争。最明显的改善在于可高速地并行执行大内存分配,降低延迟并提升吞吐量。

内部定时器效率提升,减少锁竞争和上下文切换(诸如 time.After, time.Tick, net.Conn.SetDeadline和其他相关的函数都依赖内部定时器)。这是一项性能改进,不会引起任何用户可见的更改。

编译器

此版本添加了 -d=checkptr 作为编译期选项,用于检查Go代码是否动态遵循unsafe.Pointer安全规则。默认情况下,使用 -race-msan 标志会启用此选项(在Windows上除外),并可以使用 -gcflags=all=-d=checkptr=0 禁用该选项。具体来说, -d=checkptr 会检查以下内容:

  • 当转换unsafe.Pointer到*T时,所得的指针必须被适当地对齐T。
  • 如果指针算术运算的结果指向Go堆对象,则unsafe.Pointer类型的操作数之一必须指向同一对象。

-d=checkptr目前不推荐在Windows使用,因为它会导致标准库产生错误警告。

编译器现在可以使用 -json 标志发出机器可读的关键优化日志,包括内联,逃逸分析,边界检查消除和nil检查消除。

详细的逃逸分析诊断(-m=2)现在可以再次使用了。这个功能在之前版本的逃逸分析实现中曾被移除。

现在为了遵循平台惯例,macOS二进制文件中的所有Go符号都以下划线开头。

此版本包含了由编译器插入的覆盖工具模糊测试的实验性支持。有关更多详细信息,请参见issue 14565。此API在将来的版本中可能会更改。

边界检查消除现在会读取切片创建信息,可消除索引类型小于int的检查。

核心库

新的字节序列哈希库

Go 1.14包含了新的包,hash/maphash,它提供了字节序列的哈希功能。这些哈希函数可用于构建哈希表,或者需要将任意字符串、字节序列均匀映射到64位无符号整数的数据结构。

哈希函数具备抗碰撞性,但不是密码学意义上安全的。(有加密库的使用,请参阅crypto/sha256和crypto/sha512。)

给定字节序列的哈希值在单个进程中是一致的,但在不同进程中会有所不同。

其他轻微修改

小修小改:Minor changes to the library