1. 程式人生 > >為什麼程式設計師要學GC

為什麼程式設計師要學GC

今天跟大家分享一下《垃圾回收的演算法與實現》,本書一出版即進入IT圖書銷量榜前列,由此可見大家對探祕GC的渴望。
再不學GC就老了,快來看看為什麼GC讓程式設計師心動!

什麼是GC?

GC 是 Garbage Collection 的簡稱,中文稱為“垃圾回收”。在現實世界中,“垃圾”指的是那些不讀的書、不穿的衣服等不用的東西。在 GC 中,“垃圾”的定義也是如此,GC 把程式不用的記憶體空間視為垃圾。

GC 要做兩件事。

  1. 找到記憶體空間裡的垃圾;
  2. 回收垃圾,讓程式設計師能再次利用這部分空間。

滿足這兩項功能的程式就是 GC。那麼GC 到底會給程式設計師帶來怎樣的好處呢?

沒有GC的世界

在沒有 GC 的世界裡,程式設計師必須自己手動進行記憶體管理,必須清楚地確保必要的記憶體空間,釋放不要的記憶體空間。

程式設計師在手動進行記憶體管理時,申請記憶體尚不存在什麼問題,但在釋放不要的記憶體空間時,就必須一個不漏地釋放。這非常地麻煩。如果忘記釋放記憶體空間,該記憶體空間就會發生記憶體洩露 ,即無法被使用,但它又會持續存在下去。如果將發生記憶體洩露的程式放著不管,總有一刻記憶體會被佔滿,甚至還可能導致系統崩潰。

另外,在釋放記憶體空間時,如果忘記初始化指向釋放物件的記憶體空間的指標,這個指標就會一直指向釋放完畢的記憶體空間。因為這個指標沒有指向有效的記憶體空間,處於一種懸掛的狀態,所以我們稱其為“懸垂指標”(dangling pointer)。如果在程式中錯誤地引用了懸垂指標,就會產生無法預期的 BUG。此外,懸垂指標也會導致嚴重的安全漏洞(2009 年 IE6/7 的零日漏洞曾轟動一時。——丁靈注) 。

更有甚者,還可能會出現錯誤釋放了使用中的記憶體空間的情況。一旦錯誤釋放了使用中的記憶體空間,下一次程式使用此空間時就會發生故障。大多數情況下會發生段錯誤,運氣不好的話還可能引發惡性 BUG。

上述這樣與記憶體相關的 BUG,其共通之處在於“難以確定 BUG 的原因”。我們都知道,與記憶體相關的 BUG 的潛在場所和 BUG 出現的場所在位置上(或者是時間上)不一致,所以很難確定 BUG 的原因。

有GC的世界

為了省去上述手動記憶體管理的麻煩,人們鑽研開發出了 GC。如果把記憶體管理交給計算機,程式設計師就不用去想著釋放記憶體了。在手動記憶體管理中,程式設計師要判斷哪些是不用的記憶體空間(垃圾),留意記憶體空間的壽命。但只要有 GC 在,這一切都可以交給 GC 來做。

有了 GC,程式設計師就不用再去擔心因為忘了釋放記憶體等而導致 BUG,從而大大減輕了負擔。也不用再去頭疼費事的記憶體管理。GC 能讓程式設計師告別惱人的記憶體管理,把精力集中在更本質的程式設計工作上。

GC的歷史

GC 是一門古老的技術

據筆者所知,GC 因為 Java 的釋出而一舉成名,所以很多人可能會認為 GC 是最近才有的技術。不過 GC 有著非常久遠的歷史,最初的 GC 演算法是 John McCarthy 在 1960 年釋出的。

John McCarthy 身為 Lisp 之父和人工智慧之父,是一名非常有名的黑客,事實上他同時也是 GC 之父。1960 年,McCarthy 在其論文中首次釋出了 GC 演算法。當然,當時還沒有 Garbage Collection 這個詞。在這篇論文中釋出的演算法,就是現在我們所說的 GC 標記 - 清除演算法。

引用計數法

1960 年,George E. Collins 在論文中釋出了稱為引用計數的 GC 演算法。當時 Collins 可能沒有注意到,引用計數法有個缺點,就是它不能回收“迴圈引用”。Harold McBeth在 1963 年指出了這個缺點。

GC 複製演算法

1963 年,也有“人工智慧之父”之稱的 Marvin L. Minsky 在論文中釋出了複製演算法。GC 複製演算法把記憶體分成了兩部分,這篇論文中將第二部分稱為磁帶儲存空間——不得不說帶有濃烈的時代色彩。

50 年來,GC 的根本都沒有改變

從 50 年前 GC 演算法首次釋出以來,眾多研究者對其進行了各種各樣的研究,因此許多 GC 演算法也得以釋出。但事實上,這些演算法只不過是把前文中提到的三種演算法進行組合或應用。也可以這麼說,1963 年 GC 複製演算法誕生時,GC 的根本性內容就已經完成了。

未知的第四種演算法

現在為世人所知的 GC 演算法,不過是從之前介紹的三種基本演算法中衍生出來的產物。

本書中除了細緻介紹這些基本的 GC 演算法,還會介紹應用到它們的 GC 演算法。把這些演算法全看完後,請跟筆者一起,就 GC 的課題進行思考。也許發現全新的第四種基本演算法的人,就是你。

為什麼我們現在要學 GC

有以下幾個原因。

1、GC—— 存在即合理

現在我們使用的多數程式語言都搭載有 GC。以下是幾個具體的例子。

Lisp
Java
Ruby
Python
Perl
Haskell

大家有沒有用過其中的某種程式語言?如果用過,那你在不知不覺中獲得了 GC 帶來的好處。

對程式語言來說,GC 就是一個無名英雄,默默地做著貢獻。打個比方,天鵝在水面優雅地遊動時,實際上腳蹼卻在水下拼命地划水。GC 也是如此。在由程式語言構造的美麗的原始碼這片水下,GC 在拼命地將垃圾回收再利用。

如上所述,GC 是語言處理程式中非常重要的一部分,相當於樹蔭。應該有很多人感覺“GC 幫忙回收垃圾是理所當然”的吧?

GC 基本上是高負載處理,需要花費一定的時間。打個比方,當編寫像動作遊戲這樣追求即時性的程式時,就必須儘量壓低 GC 導致的最大暫停時間。如果因為 GC 導致玩家頻繁卡頓,相信誰都會想摔手柄。碰到這種應用,我們就需要選擇最大暫停時間較短的 GC 演算法了。

再打個比方,對音樂和動畫這樣類似於編碼應用的程式來說,GC 的最大暫停時間就不那麼重要了。更為重要的是,我們必須選擇一個整體處理時間更短的演算法。

筆者深信,事先知道“這個 GC 演算法有這樣的特徵,所以它適合這個應用”對程式設計師來說很有價值。

2、多種多樣的處理程式的實現

近年來,隨著程式語言的發展,燃起了一股釋出語言處理程式的勢頭,這些語言處理程式都搭載有不同的 GC 演算法。作為語言處理程式的關鍵功能,很多人將採用了優秀的 GC 演算法作為一大賣點。

GC 效能在語言處理程式的效能評價中也是一大要素。為了正確評價 GC 的效能,對 GC 演算法的理解是不可或缺的。

3、留意記憶體空間的用法

應該有不少人是通過使用搭載 GC 的程式語言來學習程式設計的吧。本書的作者之一中村也是如此,他最初接觸的程式語言是 Java。可以說在用 Java 語言編寫程式時完全不用留意記憶體空間的用法。當然這也是多虧了 GC,這是好事,但太不留心也會招致麻煩。例如,有時會出現無意中把記憶體空間揮霍一空的情況,比如在迴圈中生成一些沒用的物件等。這是因為沒有把握好程式語言背後的記憶體管理的概念。

本書中以具體的程式語言為例,來說明程式語言中所使用的記憶體空間的結構,以及 GC 的執行。通過閱讀,我們就能在程式設計中留意記憶體空間的用法了。

4、不會過時的技術

GC 自 1960 年釋出以來,一直在吸引著頂尖工程師的目光。筆者確信,只要計算機構造不發生根本性的改變,GC 就是一門不會過時的技術。對程式設計師來說,比起學習日新月異的新技術,學習 GC 這樣的古典技術不是更幸福嗎?

5、更何況,GC 很有趣

說實話,其實筆者自己學習 GC 的時候,並沒有想過上述這些略複雜的事情,只是純粹覺得有趣。現在回過頭覺得學了 GC 真好,也只是因為它具備前面那些優點。

筆者小時候就喜歡拆點什麼東西,看看裡面是怎樣的。電視機、收音機、紅白機什麼的都拆了個遍。筆者至今都還記得看到其內部時的快感,以及瞭解其構造時的感動。或許學習 GC 也差不多是這樣。對筆者來說,研究 GC 這種理所當然存在的東西,看看它的內部是一件非常刺激的事。

6、讀者物件

本書由兩部分構成。

  1. 演算法篇
  2. 實現篇

在“演算法篇”中,我們沒有必要去詳細瞭解特定的程式語言,你只要能用任何一種語言程式設計,就能往下讀“演算法篇”。

閱讀“實現篇”需要具備 C 和 C++ 的知識。只要會用 C 的函式指標、C++ 的模板,閱讀“實現篇”就沒有什麼障礙。關於 GC 演算法的知識,讀完本書的“演算法篇”就相當夠用了。

“實現篇”中涉及各種程式語言{ Python / Java / Ruby / JavaScript },最好有一定程度的瞭解,那樣閱讀起來會比較輕鬆。

垃圾回收的演算法與實現

這裡寫圖片描述

ガベージコレクションのアルゴリズムと実裝
作者:中村成洋 相川光
譯者:丁靈
定價:99(各大網店可購買)
電子書:49.99(點選目錄購買電子書)

Ruby之父松本行弘推薦,日本天才程式設計師兼LISP黑客竹內鬱雄審校
254幅圖解,輕鬆掌握GC經典演算法
實際原始碼剖析,深入探討GC具體實現

本書分為“演算法篇”和“實現篇”兩大部分。演算法篇介紹了標記–清除演算法、引用計數法、複製演算法、標記–壓縮演算法、保守式GC、分代垃圾回收、增量式垃圾回收、RC Immix演算法等幾種重要的演算法;實現篇介紹了垃圾回收在Python、DalvikVM、Rubinius、V8等幾種語言處理程式中的具體實現。本書適合各領域程式設計師閱讀。

購買連結:
京東
噹噹
亞馬遜
互動