Go 1.3 釋出說明

Go 1.3 簡介

最新發布的 Go 1.3 版在 1.2 版釋出六個月後問世,不包含任何語言更改。它主要側重於實現工作,提供了精確的垃圾回收、對編譯器工具鏈進行重大重構以實現更快的構建(特別是對於大型專案)、全面的顯著效能改進以及對 DragonFly BSD、Solaris、Plan 9 和 Google Native Client 架構 (NaCl) 的支援。它還在記憶體模型方面對同步進行了重要改進。Go 1.3 一如既往地遵守了相容性承諾,幾乎所有內容在遷移到 1.3 後都能繼續編譯和執行,無需更改。

對支援的作業系統和架構的更改

移除對 Windows 2000 的支援

Microsoft 已於 2010 年停止支援 Windows 2000。由於它在異常處理(在 Unix 術語中稱為訊號)方面存在實現困難,因此從 Go 1.3 開始,Go 也不再支援它。

支援 DragonFly BSD

Go 1.3 現在包含對 amd64(64 位 x86)和 386(32 位 x86)架構上 DragonFly BSD 的實驗性支援。它需要 DragonFly BSD 3.6 或更高版本。

支援 FreeBSD

當時未宣佈,但自 Go 1.2 釋出以來,Go 在 FreeBSD 上的支援需要 FreeBSD 8 或更高版本。

從 Go 1.3 開始,Go 在 FreeBSD 上的支援要求核心使用 COMPAT_FREEBSD32 標誌進行編譯。

與 ARM 平臺改用 EABI 系統呼叫相結合,Go 1.3 將僅在 FreeBSD 10 上執行。x86 平臺 386 和 amd64 不受影響。

支援 Native Client

隨著 1.3 版本的釋出,Go 重新支援 Native Client 虛擬機器架構。它在 32 位 Intel 架構 (GOARCH=386) 和 64 位 Intel 架構上執行,但使用 32 位指標 (GOARCH=amd64p32)。目前尚不支援 ARM 上的 Native Client。請注意,這是 Native Client (NaCl),而不是 Portable Native Client (PNaCl)。有關 Native Client 的詳細資訊此處;如何設定 Go 版本此處有說明。

支援 NetBSD

從 Go 1.3 開始,Go 在 NetBSD 上的支援需要 NetBSD 6.0 或更高版本。

支援 OpenBSD

從 Go 1.3 開始,Go 在 OpenBSD 上的支援需要 OpenBSD 5.5 或更高版本。

支援 Plan 9

Go 1.3 現在包含對 386(32 位 x86)架構上 Plan 9 的實驗性支援。它需要 Tsemacquire 系統呼叫,該系統呼叫自 2012 年 6 月以來已在 Plan 9 中提供。

支援 Solaris

Go 1.3 現在包含對 amd64(64 位 x86)架構上 Solaris 的實驗性支援。它需要 illumos、Solaris 11 或更高版本。

記憶體模型變更

Go 1.3 記憶體模型添加了一條新規則,涉及對帶緩衝通道的傳送和接收操作,以明確帶緩衝通道可以用作簡單的訊號量,使用向通道傳送來獲取,從通道接收來釋放。這不是語言更改,只是對通訊的預期屬性的澄清。

實現和工具的更改

Go 1.3 將 goroutine 棧的實現從舊的“分段”模型更改為連續模型。當 goroutine 需要的棧空間超出可用空間時,其棧會被轉移到更大的單個記憶體塊中。這種轉移操作的開銷分攤得很好,並消除了計算反覆跨越段邊界時舊的“熱點”問題。包括效能資料在內的詳細資訊請參見此設計文件

垃圾回收器變更

一段時間以來,垃圾回收器在檢查堆中的值時一直是“精確的”;Go 1.3 版本為棧上的值添加了等效的精確度。這意味著非指標 Go 值(例如整數)永遠不會被誤認為是指標,從而防止未使用的記憶體被回收。

從 Go 1.3 開始,執行時假定指標型別的值包含指標,而其他值不包含。此假設是棧擴充套件和垃圾回收的精確行為的基礎。使用unsafe將整數儲存在指標型別值中的程式是非法的,如果執行時檢測到此行為,將會崩潰。使用unsafe將指標儲存在整數型別值中的程式也是非法的,但在執行過程中更難診斷。由於指標對執行時是隱藏的,棧擴充套件或垃圾回收可能會回收它們指向的記憶體,從而產生懸空指標

更新:使用 unsafe.Pointer 將記憶體中儲存的整數型別值轉換為指標的程式碼是非法的,必須重寫。此類程式碼可以透過 go vet 進行識別。

Map 迭代

對小型 map 的迭代不再以一致的順序進行。Go 1 定義“map 的迭代順序未指定,並且不保證每次迭代都相同。”為了防止程式碼依賴 map 迭代順序,Go 1.0 在 map 的隨機索引處開始每次 map 迭代。Go 1.1 中引入的新 map 實現忽略了對包含八個或更少條目的 map 進行隨機迭代,儘管迭代順序仍然會因系統而異。這使得人們可以編寫依賴於小型 map 迭代順序的 Go 1.1 和 Go 1.2 程式,因此只能在某些系統上可靠地工作。Go 1.3 重新引入了對小型 map 的隨機迭代,以清除這些錯誤。

更新:如果程式碼假定小型 map 的迭代順序固定,它將中斷並且必須重寫,以避免做出該假設。由於隻影響小型 map,因此問題最常出現在測試中。

作為 Go 聯結器總體大修的一部分,編譯器和聯結器已經過重構。聯結器仍然是一個 C 程式,但現在作為聯結器一部分的指令選擇階段已經透過建立名為 liblink 的新庫轉移到編譯器中。透過在首次編譯包時只執行一次指令選擇,這可以顯著加快大型專案的編譯速度。

更新:儘管這是一項重大的內部更改,但它應該對程式沒有影響。

gccgo 的狀態

GCC 4.9 版本將包含 Go 1.2 (而非 1.3) 版本的 gccgo。GCC 和 Go 專案的釋出計劃不一致,這意味著 1.3 版本將在開發分支中可用,但下一個 GCC 版本 4.10 很可能包含 Go 1.4 版本的 gccgo。

Go 命令的更改

cmd/go 命令有幾個新特性。go rungo test 子命令支援一個新的 -exec 選項,用於指定執行生成二進位制檔案的替代方式。其直接目的是支援 NaCl。

當啟用競爭檢測器時,go test 子命令的測試覆蓋率支援現在會自動將覆蓋率模式設定為 -atomic,以消除關於對覆蓋率計數器不安全訪問的錯誤報告。

即使包中沒有測試檔案,go test 子命令現在也總是構建包。以前,如果不存在測試檔案,它將不執行任何操作。

go build 子命令支援一個新的 -i 選項,用於安裝指定目標的依賴項,但不安裝目標本身。

現在支援啟用 cgo 進行交叉編譯。執行 all.bash 時,CC_FOR_TARGET 和 CXX_FOR_TARGET 環境變數分別用於指定 C 和 C++ 程式碼的交叉編譯器。

最後,go 命令現在支援透過 cgo 匯入 Objective-C 檔案(字尾為 .m)的包。

cgo 變更

處理 Go 包中 import "C" 宣告的 cmd/cgo 命令已糾正了一個可能導致某些包停止編譯的嚴重錯誤。以前,所有指向不完整結構型別的指標都轉換為 Go 型別 *[0]byte,導致 Go 編譯器無法診斷將一種結構指標傳遞給預期另一種的函式。Go 1.3 透過將每個不同的不完整結構轉換為不同的命名型別來糾正此錯誤。

給定 C 宣告 typedef struct S T,用於不完整的 struct S,一些 Go 程式碼曾利用此 bug 互換地引用 C.struct_SC.T 型別。Cgo 現在明確允許這種用法,即使對於已完成的結構型別也是如此。然而,一些 Go 程式碼也利用此 bug 將(例如)*C.FILE 從一個包傳遞到另一個包。這是不合法的,並且不再起作用:通常 Go 包應避免在其 API 中暴露 C 型別和名稱。

更新:混淆不完整型別指標或跨包邊界傳遞它們的程式碼將不再編譯,必須重寫。如果轉換是正確的且必須保留,請使用透過unsafe.Pointer進行的顯式轉換。

使用 SWIG 的程式需要 SWIG 3.0

對於使用 SWIG 的 Go 程式,現在需要 SWIG 3.0 版。cmd/go 命令現在將直接將 SWIG 生成的物件檔案連結到二進位制檔案中,而不是構建並連結共享庫。

命令列標誌解析

在 gc 工具鏈中,彙編器現在使用與 Go 標誌包相同的命令列標誌解析規則,這與傳統的 Unix 標誌解析有所不同。這可能會影響直接呼叫該工具的指令碼。例如,go tool 6a -SDfoo 現在必須寫成 go tool 6a -S -D foo。(在 Go 1.1 中,編譯器和連結器也進行了相同的更改。)

godoc 變更

當使用 -analysis 標誌呼叫時,godoc 現在對其索引的程式碼執行復雜的靜態分析。分析結果以原始碼檢視和包文件檢視呈現,包括每個包的呼叫圖以及定義與引用、型別及其方法、介面及其實現、通道上的傳送和接收操作、函式及其呼叫者以及呼叫站點與其被呼叫者之間的關係。

雜項

用於比較基準測試執行效能的程式 misc/benchcmp 已重寫。它曾是主倉庫中的一個 shell 和 awk 指令碼,現在是 go.tools 倉庫中的一個 Go 程式。文件在此

對於我們這些構建 Go 發行版的人來說,工具 misc/dist 已被移動並重命名;它現在位於主倉庫中的 misc/makerelease

效能

由於執行時和垃圾回收的更改,以及一些庫的更改,此版本 Go 二進位制檔案的效能在許多情況下得到了改進。顯著的例子包括

  • 執行時更有效地處理 defer,使每個呼叫 defer 的 goroutine 的記憶體佔用減少約兩千位元組。
  • 垃圾回收器已經提速,採用了併發清理演算法、更好的並行化和更大的頁面。累積效應可以使收集器暫停時間減少 50-70%。
  • 競爭檢測器(參見本指南)現在速度提升了約 40%。
  • 由於實現了第二個一次性執行引擎,正則表示式包regexp現在對於某些簡單表示式顯著加快。使用哪個引擎是自動選擇的;細節對使用者隱藏。

此外,執行時現在在棧轉儲中包含 goroutine 被阻塞的時長,這在除錯死鎖或效能問題時可能很有用。

標準庫的更改

新包

標準庫中新增了一個包debug/plan9obj。它實現了對 Plan 9 a.out 目標檔案的訪問。

庫的主要更改

crypto/tls 之前的一個 bug 使得有可能無意中跳過 TLS 中的驗證。在 Go 1.3 中,該 bug 已修復:必須指定 ServerName 或 InsecureSkipVerify,如果指定了 ServerName 則強制執行。這可能會破壞現有程式碼,這些程式碼錯誤地依賴於不安全的行為。

標準庫中添加了一個重要的新型別:sync.Pool。它提供了一種高效的機制,用於實現某些型別的快取,其記憶體可以由系統自動回收。

testing 包的基準測試輔助函式 B 現在有一個 RunParallel 方法,使得執行利用多 CPU 的基準測試更加容易。

更新:crypto/tls 修復可能會破壞現有程式碼,但此類程式碼是錯誤的,應進行更新。

對庫的微小更改

以下列表總結了庫的一些次要更改,主要是新增功能。有關每個更改的更多資訊,請參閱相關包文件。

  • crypto/tls 包中,新增了一個 DialWithDialer 函式,允許使用現有撥號器建立 TLS 連線,從而更容易控制撥號選項(如超時)。該包現在還在 ConnectionState 結構體中報告連線使用的 TLS 版本。
  • crypto/tls 包的 CreateCertificate 函式現在支援解析(並在其他地方支援序列化)PKCS #10 證書籤名請求。
  • fmt 包的格式化列印函式現在定義 %F 為列印浮點值時 %f 的同義詞。
  • math/big 包的 IntRat 型別現在實現了 encoding.TextMarshalerencoding.TextUnmarshaler
  • 複數冪函式 Pow 現在指定了第一個引數為零時的行為。以前未定義。詳細資訊請參見該函式的文件
  • net/http 包現在在新的 Response.TLS 欄位中公開了用於發出客戶端請求的 TLS 連線的屬性。
  • net/http 包現在允許使用 Server.ErrorLog 設定一個可選的伺服器錯誤日誌記錄器。預設情況下,所有錯誤仍然輸出到 stderr。
  • net/http 包現在支援透過 Server.SetKeepAlivesEnabled 停用伺服器上的 HTTP Keep-Alive 連線。預設情況下,伺服器繼續保持 Keep-Alive(為多個請求重用連線)。只有資源受限的伺服器或處於優雅關閉過程中的伺服器才會希望停用它們。
  • net/http 包添加了一個可選的 Transport.TLSHandshakeTimeout 設定,以限制 HTTP 客戶端請求等待 TLS 握手完成的時間。現在,它在 DefaultTransport 上也預設設定。
  • net/http 包的 DefaultTransport(由 HTTP 客戶端程式碼使用)現在預設啟用TCP Keep-Alives。其他 Transport 值(其 Dial 欄位為 nil)繼續與以前一樣工作:不使用 TCP Keep-Alives。
  • 當使用 ListenAndServeListenAndServeTLS 時,net/http 包現在為傳入的伺服器請求啟用TCP Keep-Alives。否則啟動伺服器時,不啟用 TCP Keep-Alives。
  • net/http 包現在提供一個可選的 Server.ConnState 回撥,用於掛鉤伺服器連線生命週期的各個階段(參見 ConnState)。這可用於實現限速或優雅關閉。
  • net/http 包的 HTTP 客戶端現在有一個可選的 Client.Timeout 欄位,用於指定使用客戶端發出的請求的端到端超時。
  • net/http 包的 Request.ParseMultipartForm 方法現在將在正文的 Content-Type 不是 multipart/form-data 時返回錯誤。在 Go 1.3 之前,它會靜默失敗並返回 nil。依賴於先前行為的程式碼應該進行更新。
  • net 包中,Dialer 結構體現在有一個 KeepAlive 選項,用於指定連線的 Keep-Alive 週期。
  • net/http 包的 Transport 現在即使在出錯時也會一致地關閉 Request.Body
  • os/exec 包現在實現了文件中始終提到的關於二進位制檔案相對路徑的內容。特別是,它僅在二進位制檔案的檔名不包含路徑分隔符時才呼叫 LookPath
  • reflect 包中的 SetMapIndex 函式在從 nil map 中刪除時不再 panic。
  • 如果主 goroutine 呼叫 runtime.Goexit 並且所有其他 goroutine 完成執行,程式現在總是崩潰,報告檢測到死鎖。Go 的早期版本對這種情況的處理不一致:大多數情況下報告為死鎖,但一些簡單的情況則乾淨地退出。
  • runtime/debug 包現在有一個新函式 debug.WriteHeapDump,用於寫入堆的描述。
  • strconv 包中的 CanBackquote 函式現在將 DEL 字元 U+007F 視為不可列印字元。
  • syscall 包現在提供 SendmsgN 作為 Sendmsg 的替代版本,它返回寫入的位元組數。
  • 在 Windows 上,syscall 包現在透過新增新函式 NewCallbackCDecl 以及現有函式 NewCallback 來支援 cdecl 呼叫約定。
  • testing 包現在診斷呼叫 panic(nil) 的測試,這些測試幾乎總是有錯誤的。此外,測試現在即使在失敗時也會寫入配置檔案(如果使用分析標誌呼叫)。
  • unicode 包和整個系統中的相關支援已從 Unicode 6.2.0 升級到 Unicode 6.3.0