Go 1.19 發行說明
Go 1.19 簡介
最新的 Go 版本 1.19,在 Go 1.18 釋出五個月後到來。它的主要變化集中在工具鏈、執行時和庫的實現上。和往常一樣,此次釋出保持了 Go 1 相容性承諾。我們預計幾乎所有 Go 程式都能像以前一樣繼續編譯和執行。
語言變化
語言方面只有一個小改動,即對方法宣告中型別引數的範圍進行了非常小的修正。現有程式不受影響。
記憶體模型
Go 記憶體模型已修訂,以使 Go 與 C、C++、Java、JavaScript、Rust 和 Swift 使用的記憶體模型保持一致。Go 只提供順序一致的原子操作,而不提供其他語言中更寬鬆的形式。隨著記憶體模型的更新,Go 1.19 在 sync/atomic 包中引入了新型別,使原子值更容易使用,例如 atomic.Int64 和 atomic.Pointer[T]。
移植
LoongArch 64 位
Go 1.19 添加了對 Linux 上龍芯 64 位架構 LoongArch 的支援(GOOS=linux,GOARCH=loong64)。實現的 ABI 是 LP64D。支援的最低核心版本是 5.19。
請注意,大多數現有的商用 LoongArch Linux 發行版都帶有較舊的核心,其系統呼叫 ABI 在歷史上不相容。編譯的二進位制檔案在這些系統上將無法工作,即使是靜態連結的也不行。此類不受支援系統上的使用者僅限於使用發行版提供的 Go 包。
RISC-V
riscv64 埠現在支援使用暫存器傳遞函式引數和結果。基準測試顯示 riscv64 上的典型效能提升達到 10% 或更多。
工具
文件註釋
Go 1.19 添加了對文件註釋中連結、列表和更清晰標題的支援。作為此更改的一部分,gofmt 現在會重新格式化文件註釋,使其呈現的含義更清晰。有關語法詳細資訊和 gofmt 現在突出顯示常見錯誤的描述,請參閱“Go 文件註釋”。作為此更改的另一部分,新包 go/doc/comment 提供文件註釋的解析和重新格式化,以及支援將其渲染為 HTML、Markdown 和文字。
新的 unix 構建約束
//go:build 行現在可以識別構建約束 unix。如果目標作業系統(也稱為 GOOS)是 Unix 或類 Unix 系統,則該約束滿足。對於 1.19 版本,如果 GOOS 是 aix、android、darwin、dragonfly、freebsd、hurd、illumos、ios、linux、netbsd、openbsd 或 solaris 之一,則該約束滿足。在未來的版本中,unix 約束可能會匹配更多新支援的作業系統。
Go 命令
-trimpath 標誌(如果設定)現在包含在 go build 注入 Go 二進位制檔案的構建設定中,可以使用 go version -m 或 debug.ReadBuildInfo 進行檢查。
go generate 現在在生成器的環境中明確設定 GOROOT 環境變數,以便生成器即使在使用 -trimpath 構建時也能找到正確的 GOROOT。
go test 和 go generate 現在將 GOROOT/bin 放在用於子程序的 PATH 的開頭,因此執行 go 命令的測試和生成器會將其解析到相同的 GOROOT。
go env 現在會引用其報告的 CGO_CFLAGS、CGO_CPPFLAGS、CGO_CXXFLAGS、CGO_FFLAGS、CGO_LDFLAGS 和 GOGCCFLAGS 變數中包含空格的條目。
go list -json 現在接受以逗號分隔的 JSON 欄位列表來填充。如果指定了列表,則 JSON 輸出將僅包含這些欄位,並且 go list 可能會避免計算未包含的欄位。在某些情況下,這可能會抑制原本會報告的錯誤。
go 命令現在快取載入某些模組所需的資訊,這應該會加快一些 go list 的呼叫速度。
Vet
vet 檢查器“errorsas”現在報告當 errors.As 以 *error 型別的第二個引數呼叫時(一個常見的錯誤)。
執行時
執行時現在支援軟記憶體限制。此記憶體限制包括 Go 堆和執行時管理的所有其他記憶體,不包括外部記憶體源,例如二進位制檔案本身的對映、其他語言管理的記憶體以及作業系統為 Go 程式持有的記憶體。此限制可以透過 runtime/debug.SetMemoryLimit 或等效的 GOMEMLIMIT 環境變數進行管理。該限制與 runtime/debug.SetGCPercent / GOGC 協同工作,即使 GOGC=off 也會被遵守,從而允許 Go 程式始終最大化地利用其記憶體限制,在某些情況下提高資源效率。有關更詳細的軟記憶體限制以及各種常見用例和場景的詳細指南,請參閱 GC 指南。請注意,由於外部延遲因素(例如作業系統排程),較小的記憶體限制(例如幾十兆位元組或更少)不太可能被遵守。有關更多詳細資訊,請參閱 issue 52433。較大的記憶體限制(例如幾百兆位元組或更多)是穩定的並且已準備好投入生產。
為了限制程式即時堆大小接近軟記憶體限制時 GC 抖動的影響,Go 執行時還嘗試將 GC CPU 總利用率限制在 50%(不包括空閒時間),選擇使用更多記憶體而不是阻止應用程式程序。實際上,我們預計此限制僅在特殊情況下發揮作用,新的 執行時指標 /gc/limiter/last-enabled:gc-cycle 報告上次發生此情況的時間。
當應用程式空閒到足以強制進行週期性 GC 迴圈時,執行時現在會在空閒作業系統執行緒上排程更少的 GC 工作 goroutine。
執行時現在將根據 goroutine 歷史平均堆疊使用情況分配初始 goroutine 堆疊。這避免了平均情況下一些早期堆疊增長和複製的需求,但會為低於平均水平的 goroutine 浪費最多 2 倍的空間。
在 Unix 作業系統上,匯入 os 包的 Go 程式現在會自動將開啟檔案限制(RLIMIT_NOFILE)增加到允許的最大值;也就是說,它們將軟限制更改為與硬限制匹配。這糾正了某些系統上為與使用 select 系統呼叫的非常舊的 C 程式相容而設定的人為低限制。Go 程式不受該限制的幫助,相反,即使是像 gofmt 這樣的簡單程式在並行處理許多檔案時也經常在這些系統上耗盡檔案描述符。此更改的一個影響是,反過來在子程序中執行非常舊的 C 程式的 Go 程式可能會以過高的限制執行這些程式。這可以透過在呼叫 Go 程式之前設定硬限制來糾正。
不可恢復的致命錯誤(例如併發對映寫入或未鎖定互斥鎖的解鎖)現在會列印一個更簡單的堆疊跟蹤,不包括執行時元資料(等同於致命 panic),除非 GOTRACEBACK=system 或 crash。執行時內部致命錯誤堆疊跟蹤始終包含完整的元資料,無論 GOTRACEBACK 的值如何。
ARM64 上已新增對偵錯程式注入函式呼叫的支援,使使用者在使用更新的偵錯程式時,可以在互動式除錯會話中從其二進位制檔案中呼叫函式,以利用此功能。
Go 1.18 中新增的地址消毒器支援現在更精確地處理函式引數和全域性變數。
編譯器
編譯器現在使用跳轉表來實現大型整數和字串 switch 語句。switch 語句的效能改進各不相同,但可以達到 20% 的速度提升。(僅限 GOARCH=amd64 和 GOARCH=arm64)
Go 編譯器現在需要 -p=importpath 標誌來構建可連結的目標檔案。這已由 go 命令和 Bazel 提供。任何其他直接呼叫 Go 編譯器的構建系統也需要確保傳遞此標誌。
Go 編譯器不再接受 -importmap 標誌。直接呼叫 Go 編譯器的構建系統必須改用 -importcfg 標誌。
彙編器
與編譯器一樣,彙編器現在需要 -p=importpath 標誌來構建可連結的目標檔案。這已由 go 命令提供。任何其他直接呼叫 Go 彙編器的構建系統也需要確保傳遞此標誌。
連結器
在 ELF 平臺上,連結器現在以標準 gABI 格式(SHF_COMPRESSED)發出壓縮的 DWARF 節,而不是舊版 .zdebug 格式。
標準庫
新的原子型別
sync/atomic 包定義了新的原子型別 Bool、Int32、Int64、Uint32、Uint64、Uintptr 和 Pointer。這些型別隱藏了底層值,以便所有訪問都強制使用原子 API。Pointer 也避免了在呼叫站點轉換為 unsafe.Pointer 的需要。Int64 和 Uint64 在結構體和分配的資料中自動對齊到 64 位邊界,即使在 32 位系統上也是如此。
PATH 查詢
Command 和 LookPath 不再允許從 PATH 搜尋中找到相對於當前目錄的結果。這消除了常見的安全問題來源,但也可能破壞依賴於使用 exec.Command("prog") 來運行當前目錄中名為 prog(或在 Windows 上,prog.exe)的二進位制檔案的現有程式。有關如何最好地更新此類程式的資訊,請參閱 os/exec 包文件。
在 Windows 上,Command 和 LookPath 現在遵守 NoDefaultCurrentDirectoryInExePath 環境變數,使得可以在 Windows 系統上停用 PATH 查詢中預設隱式搜尋“.”
對庫的微小更改
一如既往,庫中進行了各種小的更改和更新,始終牢記 Go 1 的相容性承諾。還有各種未在此處列出的效能改進。
archive/zip
Reader 現在會忽略 ZIP 檔案開頭的非 ZIP 資料,與大多數其他實現匹配。這對於讀取某些 Java JAR 檔案等用途是必需的。
crypto/elliptic
在無效曲線點上操作(即 IsOnCurve 方法返回 false,且從未由 Unmarshal 或對有效點操作的 Curve 方法返回的點)一直都是未定義行為,並可能導致金鑰恢復攻擊。如果將無效點提供給 Marshal、MarshalCompressed、Add、Double 或 ScalarMult,它們現在將 panic。
在 P224、P384 和 P521 曲線上的 ScalarBaseMult 操作現在快了三倍,導致某些 ECDSA 操作的速度也有類似提升。通用的(非平臺最佳化)P256 實現已替換為來自經過正式驗證模型的實現;這可能會導致 32 位平臺上的顯著減速。
crypto/rand
Read 不再在呼叫之間緩衝從作業系統獲取的隨機資料。以高頻率執行許多小讀取的應用程式可能會選擇將 Reader 封裝在 bufio.Reader 中以提高效能,同時注意使用 io.ReadFull 以確保不會發生部分讀取。
在 Plan 9 上,Read 已重新實現,用快速金鑰擦除生成器替換了 ANSI X9.31 演算法。
Prime 實現已更改為僅使用拒絕取樣,這消除了在非加密上下文中生成小素數時的偏差,消除了一個可能的小計時洩漏,並使行為與 BoringSSL 更好地對齊,同時簡化了實現。與以前的實現相比,此更改會為給定的隨機源流生成不同的輸出,這可能會破壞預期特定確定性隨機源產生特定結果的測試。為了幫助防止將來出現此類問題,該實現現在有意地對輸入流不確定。
crypto/tls
GODEBUG 選項 tls10default=1 已移除。仍然可以透過設定 Config.MinVersion 來啟用 TLS 1.0 客戶端。
TLS 伺服器和客戶端現在拒絕 TLS 握手中的重複擴充套件,如 RFC 5246 第 7.4.1.4 節和 RFC 8446 第 4.2 節所要求。
crypto/x509
CreateCertificate 不再支援建立 SignatureAlgorithm 設定為 MD5WithRSA 的證書。
CreateCertificate 不再接受負序列號。
當生成的證書沒有擴充套件時,CreateCertificate 不再發出空的 SEQUENCE。
原計劃在 Go 1.19 中移除的 GODEBUG 選項 x509sha1=1 已重新安排到未來的版本。使用它的應用程式應著手遷移。自 2017 年以來已證明對 SHA-1 的實際攻擊,並且自 2015 年以來,公共信任的證書頒發機構已不再頒發 SHA-1 證書。
ParseCertificate 和 ParseCertificateRequest 現在拒絕包含重複擴充套件的證書和 CSR。
新的 CertPool.Clone 和 CertPool.Equal 方法分別允許克隆 CertPool 和檢查兩個 CertPool 的等效性。
新的函式 ParseRevocationList 提供了一個更快、更安全的 CRL 解析器,它返回一個 RevocationList。解析 CRL 還會填充新的 RevocationList 欄位 RawIssuer、Signature、AuthorityKeyId 和 Extensions,這些欄位被 CreateRevocationList 忽略。
新的方法 RevocationList.CheckSignatureFrom 檢查 CRL 上的簽名是否是來自 Certificate 的有效簽名。
ParseCRL 和 ParseDERCRL 函式現在已棄用,取而代之的是 ParseRevocationList。Certificate.CheckCRLSignature 方法已棄用,取而代之的是 RevocationList.CheckSignatureFrom。
Certificate.Verify 的路徑構建器已 overhauled,現在應該可以在複雜場景中生成更好的鏈和/或更高效。名稱約束現在也對非葉證書強制執行。
crypto/x509/pkix
型別 CertificateList 和 TBSCertificateList 已棄用。應改用新的 crypto/x509 CRL 功能。
debug/elf
新的 EM_LOONGARCH 和 R_LARCH_* 常量支援 loong64 埠。
debug/pe
新的 File.COFFSymbolReadSectionDefAux 方法,返回一個 COFFSymbolAuxFormat5,提供了對 PE 檔案節中 COMDAT 資訊的訪問。這些都由新的 IMAGE_COMDAT_* 和 IMAGE_SCN_* 常量支援。
encoding/binary
新的介面 AppendByteOrder 提供了將 uint16、uint32 或 uint64 高效地附加到位元組切片的方法。BigEndian 和 LittleEndian 現在實現了此介面。
類似地,新的函式 AppendUvarint 和 AppendVarint 是 PutUvarint 和 PutVarint 的高效附加版本。
encoding/csv
新的方法 Reader.InputOffset 報告讀取器的當前輸入位置作為位元組偏移量,類似於 encoding/json 的 Decoder.InputOffset。
encoding/xml
新的方法 Decoder.InputPos 報告讀取器的當前輸入位置作為行和列,類似於 encoding/csv 的 Decoder.FieldPos。
flag
新的函式 TextVar 定義了一個帶有實現 encoding.TextUnmarshaler 的值的標誌,允許命令列標誌變數具有 big.Int、netip.Addr 和 time.Time 等型別。
fmt
新的函式 Append、Appendf 和 Appendln 將格式化資料附加到位元組切片。
go/parser
解析器現在將 ~x 識別為帶運算子 token.TILDE 的一元表示式,允許在不正確的上下文中使用型別約束(例如 ~int)時更好地進行錯誤恢復。
go/types
新的方法 Func.Origin 和 Var.Origin 返回在型別例項化期間建立的合成 Func 和 Var 物件的通用型別的相應 Object。
透過遞迴呼叫 Named.Underlying 或 Named.Method,不再可能生成無限數量的不同但相同的 Named 型別例項化。
hash/maphash
新的函式 Bytes 和 String 提供了一種高效的方法來雜湊單個位元組切片或字串。它們等同於使用更通用的 Hash 進行單次寫入,但它們避免了小輸入的設定開銷。
html/template
型別 FuncMap 現在是 text/template 的 FuncMap 的別名,而不是其自己的命名型別。這允許編寫在任一設定下操作 FuncMap 的程式碼。
Go 1.19.8 及更高版本禁止 ECMAScript 6 模板字面量中的操作。此行為可以透過 GODEBUG=jstmpllitinterp=1 設定還原。
image/draw
當目標和源影像都是 image.NRGBA 或都是 image.NRGBA64 時,使用 Src 運算子的 Draw 會保留非預乘 alpha 顏色。這恢復了 Go 1.18 庫最佳化意外引入的行為更改;程式碼現在與 Go 1.17 及更早版本中的行為匹配。
io
NopCloser 的結果現在實現了 WriterTo,只要其輸入實現了。
MultiReader 的結果現在無條件地實現了 WriterTo。如果任何底層讀取器未實現 WriterTo,則會進行適當模擬。
mime
僅在 Windows 上,mime 包現在會忽略記錄副檔名 .js 應具有 MIME 型別 text/plain 的登錄檔條目。這是 Windows 系統上常見的無意配置錯誤。其結果是 .js 將具有預設的 MIME 型別 text/javascript; charset=utf-8。在 Windows 上期望 text/plain 的應用程式現在必須明確呼叫 AddExtensionType。
mime/multipart
在 Go 1.19.8 及更高版本中,此包設定了其處理的 MIME 資料的大小限制,以防止惡意輸入。Reader.NextPart 和 Reader.NextRawPart 將部分中的頭數量限制為 10000,Reader.ReadForm 將所有 FileHeaders 中的頭總數限制為 10000。這些限制可以透過 GODEBUG=multipartmaxheaders 設定進行調整。Reader.ReadForm 進一步將表單中的部分數量限制為 1000。此限制可以透過 GODEBUG=multipartmaxparts 設定進行調整。
net
純 Go 解析器現在將使用 EDNS(0) 來包含建議的最大回複數據包長度,允許回覆資料包包含多達 1232 位元組(之前的最大值為 512)。萬一這導致本地 DNS 解析器出現問題,設定環境變數 GODEBUG=netdns=cgo 以使用基於 cgo 的解析器應該可以解決問題。請在問題跟蹤器上報告任何此類問題。
當 net 包函式或方法返回“I/O timeout”錯誤時,該錯誤現在將滿足 errors.Is(err, context.DeadlineExceeded)。當 net 包函式返回“operation was canceled”錯誤時,該錯誤現在將滿足 errors.Is(err, context.Canceled)。這些更改旨在使程式碼更容易測試上下文取消或超時導致 net 包函式或方法返回錯誤的情況,同時保持錯誤訊息的向後相容性。
Resolver.PreferGo 現在已在 Windows 和 Plan 9 上實現。它以前僅適用於 Unix 平臺。結合 Dialer.Resolver 和 Resolver.Dial,現在可以編寫可移植程式並在撥號時控制所有 DNS 名稱查詢。
net 包現在在 Windows 上初步支援 netgo 構建標籤。使用時,該包使用 Go DNS 客戶端(如 Resolver.PreferGo 所用),而不是向 Windows 請求 DNS 結果。然而,它從 Windows 發現的上游 DNS 伺服器在複雜的系統網路配置下可能還不正確。
net/http
ResponseWriter.WriteHeader 現在支援傳送使用者定義的 1xx 資訊性頭。
MaxBytesReader 返回的 io.ReadCloser 現在在其讀取限制超出時將返回定義的錯誤型別 MaxBytesError。
HTTP 客戶端將處理沒有 Location 頭部的 3xx 響應,將其返回給呼叫者,而不是將其視為錯誤。
net/url
新的 JoinPath 函式和 URL.JoinPath 方法透過連線路徑元素列表來建立新的 URL。
URL 型別現在區分沒有 authority 的 URL 和帶有空 authority 的 URL。例如,http:///path 有一個空 authority(主機),而 http:/path 沒有。
當 URL 具有空 authority 時,新的 URL 欄位 OmitHost 設定為 true。
os/exec
具有非空 Dir 欄位和 nil Env 的 Cmd 現在隱式為子程序設定 PWD 環境變數以匹配 Dir。
新的方法 Cmd.Environ 報告將用於執行命令的環境,包括隱式設定的 PWD 變數。
reflect
方法 Value.Bytes 現在除了切片之外還接受可定址陣列。
方法 Value.Len 和 Value.Cap 現在可以成功地對陣列指標進行操作並返回該陣列的長度,以匹配 內建 len 和 cap 函式的行為。
regexp/syntax
Go 1.18 釋出候選版本 1、Go 1.17.8 和 Go 1.16.15 包含對正則表示式解析器的安全修復,使其拒絕非常深層巢狀的表示式。由於 Go 補丁釋出不引入新的 API,在這種情況下解析器返回 syntax.ErrInternalError。Go 1.19 添加了一個更具體的錯誤 syntax.ErrNestingDepth,解析器現在返回該錯誤。
runtime
當二進位制檔案使用 -trimpath 標誌構建且程序環境中未設定 GOROOT 變數時,GOROOT 函式現在返回空字串(而不是 "go")。
runtime/metrics
新的 /sched/gomaxprocs:threads 指標報告當前的 runtime.GOMAXPROCS 值。
新的 /cgo/go-to-c-calls:calls 指標報告從 Go 呼叫 C 的總次數。此指標與 runtime.NumCgoCall 函式相同。
新的 /gc/limiter/last-enabled:gc-cycle 指標報告上次啟用 GC CPU 限制器的 GC 週期。有關 GC CPU 限制器的詳細資訊,請參閱執行時說明。
runtime/pprof
在收集 goroutine 配置檔案時,停止世界暫停時間已顯著減少,從而降低了對應用程式的整體延遲影響。
現在,所有 Unix 作業系統都在堆配置檔案中報告 MaxRSS(以前僅在 GOOS=android、darwin、ios 和 linux 中報告)。
runtime/race
除了 windows/amd64 和 openbsd/amd64(仍保留在 v2 上)之外,所有支援的平臺上的競爭檢測器都已升級到使用執行緒消毒器 v3。與 v2 相比,它現在通常快 1.5 到 2 倍,記憶體使用量減半,並且支援無限數量的 goroutine。在 Linux 上,競爭檢測器現在至少需要 glibc 版本 2.17 和 GNU binutils 2.26。
競爭檢測器現在支援 GOARCH=s390x。
對 openbsd/amd64 的競爭檢測器支援已從上游執行緒消毒器中移除,因此不太可能從 v2 更新。
runtime/trace
當同時啟用跟蹤和 CPU 探查器時,執行跟蹤將包含 CPU 探查器樣本作為瞬時事件。
sort
排序演算法已重寫為使用 模式消除快速排序,這在幾種常見場景中速度更快。
新的函式 Find 類似於 Search,但通常更容易使用:它返回一個額外的布林值,報告是否找到了相等的值。
strconv
Quote 和相關函式現在將 Unicode 字元 U+007F 引用為 \x7f,而不是 \u007f,以與其他 ASCII 值保持一致。
syscall
在 PowerPC(GOARCH=ppc64、ppc64le)上,Syscall、Syscall6、RawSyscall 和 RawSyscall6 現在始終為返回值 r2 返回 0,而不是未定義的值。
在 AIX 和 Solaris 上,Getrusage 現在已定義。
time
新的方法 Duration.Abs 提供了一種方便且安全的方式來獲取持續時間的絕對值,將 −2⁶³ 轉換為 2⁶³−1。(這種邊界情況可能由於從零時間減去最近的時間而發生。)
新的方法 Time.ZoneBounds 返回在給定時間生效的時區的開始和結束時間。它可以在迴圈中使用,以列舉給定位置所有已知的時區轉換。