大家好,我是煎魚。
感覺時間過得很快,Go1.18 釋出沒太久,泛型還在風風火火,看了看上次的投票結果,絕大部分同學還沒有在生產環境應用泛型。
這不,Go1.19 Beta1 已經正式釋出了。今天就由煎魚和大家圍觀《Go 1.19 Release Notes》中一些有意思的特性。
記憶體模型
Go 的記憶體模型已被修訂,以使 Go 與 C、C++、Java、JavaScript、Rust 和 Swift 使用的記憶體模型保持一致。Go 只提供順序一致的原子學,而不是其他語言中的任何更寬鬆的形式。
另外隨著記憶體模型的更新,Go1.19 在 sync/atomic 包中引入了新的型別,使之更容易使用原子值,如 atomic.Int64 和 atomic.Pointer[T]。
文件做了以下具體的修改:
- 記錄 Go 的整體記憶體模型描述。
- 記錄 multiword 競態會導致崩潰的情況。
- 記錄 runtime.SetFinalizer 的 happens-before。
- 記錄(或連結)更多同步型別的發生前。
- 記錄同步/原子的發生時間,匹配 C++ 的順序一致的原子(以及Java、JavaScript、Rust、Swift、C...)。
- 記錄不允許的編譯器優化。
這個只是 “修訂”,是改了文件和定義,並不涉及記憶體模型的程式碼變更。
為此 Russ Cox 寫了 Go Memory Model 的三篇文章作為系列說明:
有興趣的同學可以閱讀。
文件規範
Russ Cox 在提案《Proposal: go/doc: headings, lists, and links in Go doc comments》中,增加了對文件註釋中的連結、列表和更清晰的標題的支援。
Go 1.19 文件已經發生了變化。如下:
舊(左)與新(右)的對比圖。
手動貼連結變可跳轉:
手動分行變成無序列表區分:
這算是 Go 文件從遠古時代到新 Markdown 的一個大升級了。
構建約束
從 Go1.19 起,構建約束 unix 現在可以在 //go:build
行中被識別,能夠起到配套的約束作用。
如下格式:
//go:build unix
需要注意的是,在 1.19 版本中,如果 GOOS 是 aix、android、darwin、dragonfly、freebsd、hurd、illumos、ios、linux、netbsd、openbsd 或 solaris 中的一種,也是滿足 unix 約束的。
龍芯架構
龍芯(Loongson)是由中國科學院計算技術研究所、龍芯中科、神州龍芯等機構、公司所設計的一系列各種晶片(包括通用中央處理器、SoC、微控制器、晶片組等)。
在 Go 1.19 起增加了對 Linux 上 Loongson 64 位架構的支援(GOOS=linux,GOARCH=loong64)。
前段時間還看到龍芯中科,在科創板上市,成國產CPU第一股。國產晶片走進 Go 語言,應該也是國人推進的,太強了!
競態檢測
Go 的競態資源檢測(race detector)已經發布到 v3 版本了,將會跟隨 Go1.19 一起上線到生產可用。
與 v2 版相比,新版本的 race detector 在效能上快 1.5 倍到 2 倍,使用一半的記憶體,並且支援無限數量的 goroutine。
注:windows/amd64 和 openbsd/amd64 暫未支援。
Switch 效能提高
Go 編譯器現在使用 jump table 來實現大型整數和字串型別的 swicth 語句。switch 語句的效能改進各不相同,但可以快 20% 左右。
注:本次僅涉及 GOARCH=amd64
和 GOARCH=arm64
的變更。
執行時
堆記憶體限制
新版本的 Go 增加了 runtime.SetMemoryLimit
函式和 GOMEMLIMIT
環境變數。
關注到 runtime.SetMemoryLimit
函式為執行時提供了一個記憶體的軟限制。
函式簽名為:
func SetMemoryLimit(limit int64) int64
有了這個記憶體的軟限制後,Go 執行時將會遵守這個記憶體限制,行為包括:調整垃圾回收的頻率、更積極地將記憶體返回到底層系統等,來維持這個軟記憶體的限制。
另外即使 GOGC=off(或者是執行了 SetGCPercent(-1)
函式),也會遵守軟記憶體的限制。
有了記憶體軟限制,一般場景下,可以有效的防止由於堆記憶體分配過多,導致 Go 程式超出系統記憶體資源的最大被 KILL 的場景。
一個漏網之魚,是限制不了的。那就是它不包括:Go 二進位制使用的空間和 Go 外部的記憶體,例如:由底層系統代表程式管理的記憶體,或由同一程式中的非 Go 程式碼管理的記憶體(CGO)。
Goroutine 堆疊
新版本中 Go 執行時將根據 goroutine 的歷史平均堆疊使用率來分配初始 goroutine 堆疊(大霧,太壞了,Go 面試題的題目答案又要改了...)。
可以有效避免一些不必要的堆疊增長和複製,在低於平均水平的情況下,能節省最多 2 倍的空間浪費。
這是一個比較細緻的優化點了。
泛型改進
Go1.19 還在不斷地完善泛型的路上,這次變更來自規範《spec: adjust scope of type parameters declared by method receivers》,涉及到的是對方法宣告中型別引數的範圍做了一個非常小的修正。
原有描述:
The scope of an identifier denoting a type parameter of a function or declared by a method receiver is the function body and all parameter lists of the function.
修訂描述:
The scope of an identifier denoting a type parameter of a function or declared by a method receiver starts after the function name and ends at the end of the function body.
在 Go1.18 時,以下泛型程式碼會提示錯誤:
type T[T any] struct {}
func (T[T]) m() {} // error: T is not a generic type
在新版本(1.19 起)將會正確支援,不會發生編譯錯誤。
其他的泛型進度來講,還是在修修補補:
有待繼續觀察。
總結
在本次 Go1.19 的新版本更新中,新特性是比較少的。其中主要的原因還是泛型的各項工作給 Go 團隊帶來了不少的工作量。
今年也陸續有個別大佬離開,所以整體可用於其他新特性的時間就比較少了。
這個版本可以認為是小版本,填了一些小 “坑” 了,國內個別面試題的答案也會因此有所改變了。
文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。