Go 1.12 发行说明
Go 1.12 简介
最新的 Go 版本 1.12 在 Go 1.11 发布六个月后推出。它的主要变化在于工具链、运行时和库的实现。与以往一样,此版本保持了 Go 1 的兼容性承诺。我们预计几乎所有 Go 程序都能像以前一样继续编译和运行。
语言变化
语言规范没有变化。
移植
竞态检测器现在支持 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
。
hurd
现在是 GOOS
的一个识别值,保留用于 GNU/Hurd 系统,与 gccgo
配合使用。
Windows
Go 的新 windows/arm
端口支持在树莓派 3 等 32 位 ARM 芯片上的 Windows 10 IoT Core 上运行 Go。
AIX
Go 现在支持 POWER8 架构上的 AIX 7.2 及更高版本 (aix/ppc64
)。外部链接、cgo、pprof 和竞态检测器尚未支持。
Darwin
Go 1.12 是将在 macOS 10.10 Yosemite 上运行的最后一个版本。Go 1.13 将需要 macOS 10.11 El Capitan 或更高版本。
在 Darwin 上进行系统调用时,现在使用 libSystem
,确保与未来版本的 macOS 和 iOS 的向前兼容性。切换到 libSystem
触发了对私有 API 使用的额外 App Store 检查。由于它被视为私有,syscall.Getdirentries
现在在 iOS 上总是以 ENOSYS
失败。此外,syscall.Setrlimit
在以前成功的某些地方报告 invalid
argument
。这些后果并非 Go 特有,用户应期望与 libSystem
的实现保持行为一致。
工具
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
一起使用。现在可以使用以下命令检查变量遮蔽:
go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)
教程
Go 教程不再包含在主二进制分发中。要在本地运行教程,请手动安装它,而不是运行 go
tool
tour
go get -u golang.org/x/tour
tour
构建缓存要求
构建缓存现在是消除 $GOPATH/pkg
的一步。设置环境变量 GOCACHE=off
将导致写入缓存的 go
命令失败。
仅限二进制包
Go 1.12 是最后一个支持仅限二进制包的版本。
Cgo
Go 1.12 会将 C 类型 EGLDisplay
转换为 Go 类型 uintptr
。此更改类似于 Go 1.10 及更高版本处理 Darwin 的 CoreFoundation 和 Java 的 JNI 类型的方式。有关更多信息,请参阅 cgo 文档。
使用 Cgo 的包中不再接受被修改的 C 名称。请改用 Cgo 名称。例如,使用文档化的 cgo 名称 C.char
,而不是 cgo 生成的被修改名称 _Ctype_char
。
模块
当 GO111MODULE
设置为 on
时,go
命令现在支持在模块目录之外进行模块感知操作,前提是这些操作不需要解析相对于当前目录的导入路径或显式编辑 go.mod
文件。诸如 go
get
、go
list
和 go
mod
download
等命令的行为就像在一个初始为空的模块中一样。在此模式下,go
env
GOMOD
报告系统的空设备(/dev/null
或 NUL
)。
下载和提取模块的 go
命令现在可以安全地并发调用。模块缓存 (GOPATH/pkg/mod
) 必须位于支持文件锁定的文件系统中。
go.mod
文件中的 go
指令现在指示该模块中文件使用的语言版本。如果不存在现有版本,它将设置为当前版本 (go
1.12
)。如果模块的 go
指令指定了比正在使用的工具链更新的版本,go
命令将尝试构建包,并且只有在构建失败时才会指出不匹配。
go
指令的这种更改意味着,如果您使用 Go 1.12 构建一个模块,从而在 go.mod
文件中记录 go 1.12
,那么在尝试使用 Go 1.11 到 Go 1.11.3 构建同一模块时将收到错误。Go 1.11.4 或更高版本将正常工作,早于 Go 1.11 的版本也将正常工作。如果您必须使用 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
的结果变得更加重要。
// Old code which no longer works correctly (it will miss inlined call frames).
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())
}
}
// New code which will work correctly.
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.CallersFrames
和 runtime.Stack
报告。它们也不会在 panic 堆栈跟踪中打印。此更改使 gc
工具链与 gccgo
工具链保持一致,后者已从堆栈跟踪中省略了此类包装器。这些 API 的客户端可能需要调整以适应缺失的帧。对于必须在 1.11 和 1.12 版本之间互操作的代码,可以将方法表达式 x.M
替换为函数字面量 func (...) { x.M(...) }
。
编译器现在接受 -lang
标志来设置要使用的 Go 语言版本。例如,-lang=go1.8
会导致编译器在程序使用类型别名(Go 1.9 中添加)时发出错误。在 Go 1.12 之前进行的语言更改未始终强制执行。
编译器工具链现在使用不同的约定来调用 Go 函数和汇编函数。这对于用户来说应该是不可见的,除了同时跨越 Go 和汇编以及跨越包边界的调用。如果链接导致“relocation target not defined for ABIInternal (but is defined for ABI0)”之类的错误,请参阅 ABI 设计文档的兼容性部分。
编译器生成的 DWARF 调试信息有许多改进,包括参数打印和变量位置信息的改进。
Go 程序现在还在 linux/arm64
上维护堆栈帧指针,以利于 perf
等分析工具。帧指针维护会产生少量运行时开销,开销因情况而异,但平均约为 3%。要构建不使用帧指针的工具链,请在运行 make.bash
时设置 GOEXPERIMENT=noframepointer
。
过时的“安全”编译器模式(由 -u
gcflag 启用)已删除。
godoc
和 go
doc
在 Go 1.12 中,godoc
不再具有命令行界面,而仅是一个 Web 服务器。用户应该使用 go
doc
获取命令行帮助输出。Go 1.12 是最后一个包含 godoc
Web 服务器的版本;在 Go 1.13 中,它将通过 go
get
提供。
go
doc
现在支持 -all
标志,它将打印所有导出的 API 及其文档,就像以前的 godoc
命令行一样。
go
doc
现在还包含 -src
标志,它将显示目标的源代码。
Trace
跟踪工具现在支持绘制变异器利用率曲线,包括对执行跟踪的交叉引用。这些对于分析垃圾收集器对应用程序延迟和吞吐量的影响非常有用。
汇编器
在 arm64
上,平台寄存器从 R18
重命名为 R18_PLATFORM
,以防止意外使用,因为操作系统可以选择保留此寄存器。
运行时
当堆的大部分仍然存活时,Go 1.12 显著提高了清扫性能。这减少了垃圾收集后立即的分配延迟。
Go 运行时现在更积极地将内存释放回操作系统,特别是在无法重用现有堆空间的大量分配之后。
Go 运行时的计时器和截止时间代码更快,并且在 CPU 数量更多时扩展性更好。特别是,这提高了操纵网络连接截止时间的性能。
在 Linux 上,运行时现在使用 MADV_FREE
释放未使用的内存。这更有效率,但可能导致报告的 RSS 更高。内核将在需要时回收未使用的数据。要恢复到 Go 1.11 的行为 (MADV_DONTNEED
),请设置环境变量 GODEBUG=madvdontneed=1
。
将 cpu.extension=off 添加到 GODEBUG 环境变量现在会禁用标准库和运行时中可选 CPU 指令集扩展的使用。这在 Windows 上尚不支持。
Go 1.12 通过修复大量堆分配的过度计数,提高了内存配置文件的准确性。
堆栈回溯、runtime.Caller
和 runtime.Callers
不再包含编译器生成的初始化函数。在全局变量初始化期间进行堆栈回溯现在将显示一个名为 PKG.init.ializers
的函数。
标准库
TLS 1.3
Go 1.12 在 crypto/tls
包中添加了可选的 TLS 1.3 支持,具体由 RFC 8446 指定。可以通过将值 tls13=1
添加到 GODEBUG
环境变量来启用它。它将在 Go 1.13 中默认启用。
要协商 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
,则偏好顺序基于可用的硬件。
目前不支持作为客户端或服务器的早期数据(也称为“0-RTT 模式”)。此外,Go 1.12 服务器不支持在客户端发送意外早期数据时跳过。由于 TLS 1.3 0-RTT 模式涉及客户端保留有关哪些服务器支持 0-RTT 的状态,Go 1.12 服务器不能成为负载均衡池的一部分,其中一些其他服务器确实支持 0-RTT。如果将域从支持 0-RTT 的服务器切换到 Go 1.12 服务器,则必须在切换之前至少在颁发的会话票证的生命周期内禁用 0-RTT,以确保不间断运行。
在 TLS 1.3 中,客户端是握手中最后发言的一方,因此如果它导致服务器上发生错误,该错误将由第一次 Read
在客户端上返回,而不是由 Handshake
返回。例如,如果服务器拒绝客户端证书,就会出现这种情况。同样,会话票证现在是握手后消息,因此只有在客户端第一次 Read
时才会收到。
对库的微小更改
与往常一样,库中有各种微小的更改和更新,这些都是在遵守 Go 1 兼容性承诺的前提下进行的。
bufio
如果 Peek
之后调用 Reader
的 UnreadRune
和 UnreadByte
方法,它们现在将返回错误。
bytes
新函数 ReplaceAll
返回一个字节切片的副本,其中所有非重叠的值实例都被另一个值替换。
零值 Reader
的指针现在在功能上等同于 NewReader
(nil)
。在 Go 1.12 之前,前者在所有情况下都不能替代后者。
crypto/rand
当 Reader.Read
首次阻塞超过 60 秒等待从内核读取熵时,将打印警告到标准错误。
在 FreeBSD 上,Reader
现在使用 getrandom
系统调用(如果可用),否则使用 /dev/urandom
。
crypto/rc4
此版本删除了汇编实现,只留下纯 Go 版本。Go 编译器生成的代码略好或略差,具体取决于 CPU。RC4 不安全,应仅用于与旧系统兼容。
crypto/tls
如果客户端发送的初始消息看起来不像 TLS,服务器将不再回复警报,并且它将在 RecordHeaderError
的新字段 Conn
中公开底层 net.Conn
。
database/sql
现在可以通过将 *Rows
值传递给 Row.Scan
方法来获取查询游标。
expvar
fmt
现在以按键排序的顺序打印映射,以方便测试。排序规则如下:
- 如果适用,nil 比较为低
- 整数、浮点数和字符串按 < 排序
- NaN 小于非 NaN 浮点数
- 布尔值 false 在 true 之前
- 复数先比较实部,再比较虚部
- 指针按机器地址比较
- 通道值按机器地址比较
- 结构体依次比较每个字段
- 数组依次比较每个元素
- 接口值首先根据描述具体类型的
reflect.Type
进行比较,然后根据前面规则描述的具体值进行比较。
打印映射时,非自反键值(如 NaN
)以前显示为 <nil>
。从这个版本开始,会打印正确的值。
go/doc
为了解决 cmd/doc
中的一些悬而未决的问题,此包有一个新的 Mode
位 PreserveAST
,它控制是否清除 AST 数据。
go/token
File
类型有一个新的 LineStart
字段,它返回给定行的起始位置。这在偶尔处理非 Go 文件(例如汇编)但希望使用 token.Pos
机制识别文件位置的程序中特别有用。
图像
RegisterFormat
函数现在可以安全地并发使用。
image/png
颜色少于 16 种的调色板图像现在编码为更小的输出。
io
新的 StringWriter
接口包装了 WriteString
函数。
math
函数 Sin
、Cos
、Tan
和 Sincos
现在对巨大的参数应用 Payne-Hanek 范围缩减。这会产生更准确的结果,但它们不会与早期版本的结果逐位相同。
math/bits
新的扩展精度操作 Add
、Sub
、Mul
和 Div
可用于 uint
、uint32
和 uint64
版本。
net
Dialer.DualStack
设置现在被忽略并已弃用;RFC 6555 快速回退(“Happy Eyeballs”)现在默认启用。要禁用,请将 Dialer.FallbackDelay
设置为负值。
同样,如果 Dialer.KeepAlive
为零,TCP 保活现在默认启用。要禁用,请将其设置为负值。
在 Linux 上,当从 UnixConn
复制到 TCPConn
时,现在使用 splice
系统调用。
net/http
HTTP 服务器现在会以纯文本“400 Bad Request”响应拒绝发送到 HTTPS 服务器的误导性 HTTP 请求。
新的 Client.CloseIdleConnections
方法会调用 Client
底层 Transport
的 CloseIdleConnections
(如果存在)。
Transport
不再拒绝声明 HTTP Trailers 但不使用分块编码的 HTTP 响应。相反,声明的 Trailers 现在只是被忽略。
Transport
不再像 Go 1.10 和 Go 1.11 那样严格处理 HTTP/2 服务器 advertised 的 MAX_CONCURRENT_STREAMS
值。默认行为现在恢复到 Go 1.9 的方式:与服务器的每个连接最多可以有 MAX_CONCURRENT_STREAMS
个活动请求,然后根据需要创建新的 TCP 连接。在 Go 1.10 和 Go 1.11 中,http2
包会阻塞并等待请求完成,而不是创建新连接。要恢复更严格的行为,请直接导入 golang.org/x/net/http2
包并设置 Transport.StrictMaxConcurrentStreams
为 true
。
net/url
Parse
、ParseRequestURI
和 URL.Parse
现在对包含 ASCII 控制字符(包括 NULL、制表符和换行符)的 URL 返回错误。
net/http/httputil
新的 ReverseProxy
现在会自动代理 WebSocket 请求。
os
新的 ProcessState.ExitCode
方法返回进程的退出代码。
ModeCharDevice
已添加到 ModeType
位掩码中,允许在用 ModeType
掩码 FileMode
时恢复 ModeDevice | ModeCharDevice
。
新函数 UserHomeDir
返回当前用户的主目录。
RemoveAll
现在支持大多数 Unix 系统上长度超过 4096 个字符的路径。
File.Sync
现在在 macOS 上使用 F_FULLFSYNC
来正确地将文件内容刷新到永久存储。这可能会导致该方法运行速度比以前的版本慢。
File
现在支持 SyscallConn
方法,该方法返回 syscall.RawConn
接口值。这可用于对底层文件描述符调用系统特定操作。
path/filepath
IsAbs
函数现在在传递 Windows 上的保留文件名(例如 NUL
)时返回 true。保留名称列表。
reflect
新的 MapIter
类型是用于遍历映射的迭代器。此类型通过 Value
类型的新 MapRange
方法公开。它遵循与 range 语句相同的迭代语义,使用 Next
前进迭代器,以及 Key
/Value
访问每个条目。
regexp
Copy
不再是避免锁竞争所必需的,因此它被部分弃用注释。如果使用 Copy
的原因是制作具有不同 Longest
设置的两个副本,那么它仍然可能是合适的。
runtime/debug
新的 BuildInfo
类型公开了从正在运行的二进制文件读取的构建信息,仅适用于使用模块支持构建的二进制文件。这包括主包路径、主模块信息和模块依赖项。此类型通过 BuildInfo
上的 ReadBuildInfo
函数提供。
strings
新函数 ReplaceAll
返回一个字符串的副本,其中所有非重叠的值实例都被另一个值替换。
零值 Reader
的指针现在在功能上等同于 NewReader
(nil)
。在 Go 1.12 之前,前者在所有情况下都不能替代后者。
新的 Builder.Cap
方法返回构建器底层字节切片的容量。
字符映射函数 Map
、Title
、ToLower
、ToLowerSpecial
、ToTitle
、ToTitleSpecial
、ToUpper
和 ToUpperSpecial
现在始终保证返回有效的 UTF-8。在早期版本中,如果输入是无效的 UTF-8 但无需应用字符替换,这些例程会错误地返回未修改的无效 UTF-8。
syscall
FreeBSD 12 现在支持 64 位 inode。一些类型已相应调整。
现在支持兼容版本的 Windows 上的 Unix 套接字(AF_UNIX
)地址族。
Windows 上引入了新函数 Syscall18
,允许最多 18 个参数的调用。
syscall/js
Callback
类型和 NewCallback
函数已重命名;它们现在分别称为 Func
和 FuncOf
。这是一个破坏性更改,但 WebAssembly 支持仍处于实验阶段,尚未受 Go 1 兼容性承诺的约束。任何使用旧名称的代码都需要更新。
如果某个类型实现了新的 Wrapper
接口,ValueOf
将使用它返回该类型的 JavaScript 值。
零 Value
的含义已更改。它现在表示 JavaScript 的 undefined
值,而不是数字零。这是一个破坏性更改,但 WebAssembly 支持仍处于实验阶段,尚未受 Go 1 兼容性承诺的约束。任何依赖零 Value
意味着数字零的代码都需要更新。
新的 Value.Truthy
方法报告给定值的 JavaScript“真值”。
testing
-benchtime
标志现在支持设置显式迭代计数,而不是时间,当值以“x
”结尾时。例如,-benchtime=100x
运行基准测试 100 次。
text/template
执行模板时,错误中不再截断长上下文值。
正在执行 "tmpl" at <.very.deep.context.v...>: 映射中没有 "notpresent" 的条目
现在是
正在执行 "tmpl" at <.very.deep.context.value.notpresent>: 映射中没有 "notpresent" 的条目
如果模板调用的用户定义函数发生 panic,该 panic 现在会被 Execute
或 ExecuteTemplate
方法捕获并作为错误返回。
time
$GOROOT/lib/time/zoneinfo.zip
中的时区数据库已更新到版本 2018i。请注意,此 ZIP 文件仅在操作系统未提供时区数据库时使用。
unsafe
将 nil unsafe.Pointer
转换为 uintptr
并通过算术转换回来是无效的。(这已经无效,但现在会导致编译器行为异常。)