在 Go 中使用 finalizers 比不使用更好
Go 擁有 finalizers,它們支援程式呼叫一些程式碼並作為一個物件來進行垃圾回收。然而,很多人不太喜歡 finalizers,並且通常的建議是完全避免它們 ([比如](https://twitter.com/davecheney/status/790343722865090560))。最近,David Crawshaw 在[《Finalizers 慘案》](https://crawshaw.io/blog/tragedy-of-finalizers) 一文中指出 finalizers 的眾多弊端並展示一個依賴他們會導致失敗的案例。我差不多同意其上述所有觀點,但與此同時,我自己已經在[一個 Go 包訪問 Solaris/Illumos kstats](https://github.com/siebenmann/go-kstat) 中使用 finalizers,接下來我將對此用法進行辯護。 我使用 finalizers 來避免人們不正確使用[我的 API](https://github.com/siebenmann/go-kstat/blob/master/kstat-godoc.txt) 時造成不可見的記憶體洩露。理論上,當你呼叫我的 jar 包並返回一個神奇的 token,它持有對一些 C-allocated 記憶體的唯一引用。當你使用此 token 後,你應該呼叫一個方法關閉它,以此來釋放 C-allocated 記憶體。通常人們會在 API 用法和物件生命週期上犯錯。在不使用 finalizer 的情形下,如果一個 token 超過作用範圍並在垃圾回收中未被回收,我們將永久洩露此 C-allocated 記憶體。諸如所有記憶體和資源洩露此類事情,這將成為一個極其容易忽視且致命的洩露,因為從 Go 標準上它將完全不可見。目前也沒有任何一個通用 Go 標準記憶體洩露工具幫助你解決上述問題(且鑑於 Go 的存在,我認為通用 C 洩露查詢工具會產生嚴重的問題)。 一方面,此處使用一個 finalizer 是一種實用的決定;它能保障人們在使用我的 jar 包時,遠離某些用法錯誤,這些錯誤會造成一些難以解決的問題。另一方面,我認為此處使用 finalizers 正是 Go 的廣泛意義所在。作為一門垃圾回收語言,Go 從本質上決定了管理物件生命週期過於困難,需要大量工作,且太容易犯錯。使用 finalizer 完美處理記憶體問題是存在一定特殊性的,但並不適用於處理除純粹從實用角度出發之外的任何其他資源。 (與此同時,這些實用角度是真實存在的 ; 正如 David Crawshaw 所言,依賴記憶體垃圾回收來垃圾收集被耗盡之前的其他資源是極其危險的。這一點甚至對於我的示例在某種程度上也是值得懷疑的,因為 C-allocated 記憶體並未施壓於 Go 垃圾回收器。) David Crawshaw 跟進發表了一篇文章 -[《銳利的 Go Finalizers》](https://crawshaw.io/blog/sharp-edged-finalizers), 這篇文章中他主張,當人們不能正確使用你的 APIs 時使用 finalizers 來強制恐慌。你可以這麼幹,但對我來說這有點不像 Go 的風格。總之我認為當且僅當不正確使用你的 API 的後果是特別嚴重的話,你才應該採用這種方式(比如說,潛在的資料丟失,由於你忘了提交資料庫事務然後檢查出錯誤)。 一般而言,我不認為我這種 finalizers 使用方式本身是有意去避免洩漏的。通常從你不再需要這些資源(kstat 令牌,開放檔案,或者你擁有的資源)那一刻起,程式將發生記憶體洩漏,直到 Go 垃圾回收呼叫你的 finalizer(如果之前是這麼幹的話),因為這些資源一直都存在,但沒有一個在被使用或需要。finalizers 所做的就是讓這些洩漏從理論上成為暫時,而不是明確地永久。換句話說,這是一個可修復的洩漏而不是不可修復的洩漏。 PS: 此觀點當然不是我原創的。比如說,[非官方 Go FAQ](https://go101.org/article/unofficial-faq.html) 闡述[finalizers 的主要用途](https://go101.org/article/unofficial-faq.html#finalizers),且有標準庫中*os.File finalizer 的例子。 (此觀點已經在我腦海中有一段時間了,但 David Crawshaw 的文章提供了一個便捷的提示,而我並沒有想到在這種情形下使用 finalizers 來強制出現一個嚴重的錯誤。)
via:ofollow,noindex">https://utcc.utoronto.ca/~cks/space/blog/programming/GoFinalizersStopLeaks
作者:Chris Siebenmann 譯者:MAXAmbitious 校對:polaris1119