對比Ruby和Python的垃圾回收
注:這篇文章基于我在布達佩斯的RuPy大會(huì )上所作的演講。我覺(jué)得與其直接將幻燈片發(fā)布出來(lái),不如在我還有印象的時(shí)候將它寫(xiě)成博客來(lái)的更有意義。同樣,我會(huì )在將來(lái)發(fā)布RuPy大會(huì )的視頻鏈接。我計劃將在RubyConf大會(huì )上發(fā)表類(lèi)似的演講,除了有關(guān)于Python的部分,并且將對比MRI,JRuby以及Rubinius的垃圾回收器是怎樣工作的。
本文引用地址:http://dyxdggzs.com/article/201609/304199.htm如果想要對Ruby垃圾回收器以及內部原理有更加深入的了解,你可以在我即將出版的新書(shū)《Ruby Under a Microscope》中找到答案。

如果算法和業(yè)務(wù)邏輯是一個(gè)人的大腦,那么垃圾回收機制是人體的哪個(gè)器官呢?
在”Ruby Python”大會(huì )上,我想對比Ruby和Python內部的垃圾回收機制是一件很有意思的事情。在開(kāi)始之前,我們?yōu)槭裁匆懻摾厥諜C制呢?畢竟這是一個(gè)最迷人的,最令人激動(dòng)的主題,不是嗎?你們有多少人對垃圾回收機制感到興奮?(許多的大會(huì )參與者竟然舉起了雙手!)
最近,在Ruby社區中有一篇帖子,關(guān)于怎樣通過(guò)修改Ruby GC的設置來(lái)提高單元測試的速度。這棒極了!通過(guò)減少GC垃圾回收的處理來(lái)提高測試的速度,這是一件很好的事情,但是不怎的,GC不會(huì )真正的讓我感到興奮。就如咋一看就感覺(jué)令人厭煩,枯燥的技術(shù)帖子。
事實(shí)上,垃圾回收是一個(gè)令人著(zhù)迷的主題:垃圾回收算法不僅是計算機科學(xué)歷史一個(gè)重要的部分,更是前沿研究的一個(gè)主題。例如,MRI Ruby解釋器使用的”Mark Sweep”算法已經(jīng)超過(guò)了50年的歷史,與此同時(shí),在Rubinius解釋器中使用的一種垃圾回收算法,是在Ruby中的另一種實(shí)現方式,這種算法僅僅是在2008才被研究出來(lái)。
然而,”垃圾回收”的這個(gè)名稱(chēng),是非常的不恰當的。
應用程序的心臟
垃圾回收系統要做的不僅僅是”回收垃圾”。事實(shí)上,它主要完成三個(gè)重要任務(wù):
為新的對象分配內存
標記垃圾對象
回收垃圾對象占用的內存
想象你的應用程序是一個(gè)人的身體:所有你寫(xiě)的優(yōu)雅的代碼,你的商業(yè)邏輯,你的算法,將會(huì )成為你的應用程序的大腦或智能。與此類(lèi)似的,你認為垃圾回收器會(huì )成為身體的哪一個(gè)部分呢?(我從大會(huì )的聽(tīng)眾中得到了很多有趣的答案:腎,白細胞)

我認為垃圾回收器是一個(gè)應用的心臟。正如心臟為身體的其他部分提供血液和養料一樣,垃圾回收器提供內存和對象供程序使用。如果你的心臟停跳,你將活不了幾秒。如果垃圾回收器停止運行或者變慢,就像動(dòng)脈阻塞一樣,你的程序將變的慢下來(lái)最后死掉!
一個(gè)簡(jiǎn)單的例子
通過(guò)例子來(lái)驗證理論是一種很好的方式。這里有一個(gè)簡(jiǎn)單的類(lèi),用Python和Ruby寫(xiě)成,我們可以將它們作為一個(gè)簡(jiǎn)單的例子:

于此同時(shí),兩種代碼如此相似讓我感到非常吃驚:Python和Ruby在表達相同的語(yǔ)義時(shí)幾乎沒(méi)有差別。但是,兩種語(yǔ)言的內部實(shí)現方式是否相同呢?
空閑對象鏈表
在上面的代碼中,當我們調用了Node.new(1)之后,ruby將會(huì )做什么?也就是說(shuō),Ruby怎樣創(chuàng )建一個(gè)新的對象?
令人驚訝的是,Ruby做的事情非常少!事實(shí)上,在代碼運行之前,Ruby解釋器會(huì )提前創(chuàng )建成千上萬(wàn)的對象放置到一個(gè)鏈表中,這個(gè)鏈表被稱(chēng)為”空閑對象鏈表”(free list)??臻e對象鏈表(`free list`)在概念上看起來(lái)像下面的樣子:

每一個(gè)白色方塊可以想象成一個(gè)預創(chuàng )建的,沒(méi)有使用的Ruby對象。當我們調用Node.new,Ruby簡(jiǎn)單的使用一個(gè)對象,并且將它的引用返回給我們:

在上圖中,左邊的灰色方塊代表一個(gè)活躍的Ruby對象,被我們的代碼所使用,而其余的白色方塊代碼沒(méi)有使用的對象。(注意:當然,圖中是一種簡(jiǎn)化的實(shí)現版本。事實(shí)上,Ruby將會(huì )使用另外一個(gè)對象保存字符串”ABC”,使用第三個(gè)對象保存Node的定義,以及其他的對象保存代碼處理過(guò)的抽象語(yǔ)法數”AST”,等待。)
如果我們再次調用Node.new,Ruby僅僅返回另外一個(gè)對象的引用。


約翰麥卡錫在1960年在Lisp中首次實(shí)現了垃圾回收機制
這中使用預創(chuàng )建對象鏈表的簡(jiǎn)單算法發(fā)明于50多年前,它的作者是傳說(shuō)中的計算機科學(xué)家,約翰麥卡錫,正是他實(shí)現了最初的Lisp解釋器。Lisp不僅是第一個(gè)函數式編程語(yǔ)言,并且包含了計算機科學(xué)中許多突破性的進(jìn)展。其中之一便是通過(guò)垃圾回收機制自動(dòng)管理內存。
標準版Ruby,也就是”Matz’s Ruby Interpreter”(MRI),使用了一種類(lèi)似于約翰麥卡錫在1960年實(shí)現的Lisp的垃圾回收算法。就像Lisp一樣,Ruby會(huì )預先創(chuàng )建對象并且在你創(chuàng )建對象或值的時(shí)候返回對象的引用。
在Python中分配對象內存
從上面我們可以看出,Ruby會(huì )預先創(chuàng )建對象,并且保存在空閑對象鏈表(free list)中。那么Python呢?
評論