溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進行對比Ruby和Python的垃圾回收

發(fā)布時間:2021-10-28 16:49:05 來源:億速云 閱讀:151 作者:柒染 欄目:編程語言

本篇文章為大家展示了如何進行對比Ruby和Python的垃圾回收,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

我覺得與其直接將幻燈片發(fā)布出來,不如在我還有印象的時候?qū)⑺鼘懗刹┛蛠淼母幸饬x。除了有關(guān)于Python的部分,并且將對比MRI,JRuby以及Rubinius的垃圾回收器是怎樣工作的。

如果算法和業(yè)務(wù)邏輯是一個人的大腦,那么垃圾回收機制是人體的哪個器官呢?

在”Ruby Python”大會上,我想對比Ruby和Python內(nèi)部的垃圾回收機制是一件很有意思的事情。在開始之前,我們?yōu)槭裁匆懻摾厥諜C制呢?畢竟這是一個最迷人的,最令人激動的主題,不是嗎?你們有多少人對垃圾回收機制感到興奮?(許多的大會參與者竟然舉起了雙手!)

最近,在Ruby社區(qū)中有一篇帖子,關(guān)于怎樣通過修改Ruby GC的設(shè)置來提高單元測試的速度。這棒極了!通過減少GC垃圾回收的處理來提高測試的速度,這是一件很好的事情,但是不怎的,GC不會真正的讓我感到興奮。就如咋一看就感覺令人厭煩,枯燥的技術(shù)帖子。

事實上,垃圾回收是一個令人著迷的主題:垃圾回收算法不僅是計算機科學歷史一個重要的部分,更是前沿研究的一個主題。例如,MRI Ruby解釋器使用的”Mark Sweep”算法已經(jīng)超過了50年的歷史,與此同時,在Rubinius解釋器中使用的一種垃圾回收算法,是在Ruby中的另一種實現(xiàn)方式,這種算法僅僅是在2008才被研究出來。

然而,”垃圾回收”的這個名稱,是非常的不恰當?shù)摹?/p>

應(yīng)用程序的心臟

垃圾回收系統(tǒng)要做的不僅僅是”回收垃圾”。事實上,它主要完成三個重要任務(wù):

  • 為新的對象分配內(nèi)存

  • 標記垃圾對象

  • 回收垃圾對象占用的內(nèi)存

想象你的應(yīng)用程序是一個人的身體:所有你寫的優(yōu)雅的代碼,你的商業(yè)邏輯,你的算法,將會成為你的應(yīng)用程序的大腦或智能。與此類似的,你認為垃圾回收器會成為身體的哪一個部分呢?(我從大會的聽眾中得到了很多有趣的答案:腎,白細胞)

如何進行對比Ruby和Python的垃圾回收

我認為垃圾回收器是一個應(yīng)用的心臟。正如心臟為身體的其他部分提供血液和養(yǎng)料一樣,垃圾回收器提供內(nèi)存和對象供程序使用。如果你的心臟停跳,你將活不了幾秒。如果垃圾回收器停止運行或者變慢,就像動脈阻塞一樣,你的程序?qū)⒆兊穆聛?**死掉!

一個簡單的例子

通過例子來驗證理論是一種很好的方式。這里有一個簡單的類,用Python和Ruby寫成,我們可以將它們作為一個簡單的例子:

如何進行對比Ruby和Python的垃圾回收

于此同時,兩種代碼如此相似讓我感到非常吃驚:Python和Ruby在表達相同的語義時幾乎沒有差別。但是,兩種語言的內(nèi)部實現(xiàn)方式是否相同呢?

空閑對象鏈表

在上面的代碼中,當我們調(diào)用了Node.new(1)之后,ruby將會做什么?也就是說,Ruby怎樣創(chuàng)建一個新的對象?

令人驚訝的是,Ruby做的事情非常少!事實上,在代碼運行之前,Ruby解釋器會提前創(chuàng)建成千上萬的對象放置到一個鏈表中,這個鏈表被稱為”空閑對象鏈表”(free list)。空閑對象鏈表(`free list`)在概念上看起來像下面的樣子:

如何進行對比Ruby和Python的垃圾回收

每一個白色方塊可以想象成一個預(yù)創(chuàng)建的,沒有使用的Ruby對象。當我們調(diào)用Node.new,Ruby簡單的使用一個對象,并且將它的引用返回給我們:

如何進行對比Ruby和Python的垃圾回收

在上圖中,左邊的灰色方塊代表一個活躍的Ruby對象,被我們的代碼所使用,而其余的白色方塊代碼沒有使用的對象。(注意:當然,圖中是一種簡化的實現(xiàn)版本。事實上,Ruby將會使用另外一個對象保存字符串”ABC”,使用第三個對象保存Node的定義,以及其他的對象保存代碼處理過的抽象語法數(shù)”AST”,等待。)

如果我們再次調(diào)用Node.new,Ruby僅僅返回另外一個對象的引用。

如何進行對比Ruby和Python的垃圾回收

如何進行對比Ruby和Python的垃圾回收

約翰麥卡錫在1960年在Lisp中***實現(xiàn)了垃圾回收機制

這中使用預(yù)創(chuàng)建對象鏈表的簡單算法發(fā)明于50多年前,它的作者是傳說中的計算機科學家,約翰麥卡錫,正是他實現(xiàn)了最初的Lisp解釋器。Lisp不僅是***個函數(shù)式編程語言,并且包含了計算機科學中許多突破性的進展。其中之一便是通過垃圾回收機制自動管理內(nèi)存。

標準版Ruby,也就是”Matz’s Ruby Interpreter”(MRI),使用了一種類似于約翰麥卡錫在1960年實現(xiàn)的Lisp的垃圾回收算法。就像Lisp一樣,Ruby會預(yù)先創(chuàng)建對象并且在你創(chuàng)建對象或值的時候返回對象的引用。

在Python中分配對象內(nèi)存

從上面我們可以看出,Ruby會預(yù)先創(chuàng)建對象,并且保存在空閑對象鏈表(free list)中。那么Python呢?

當然Python內(nèi)部也會由于各種原因使用空閑對象鏈表(它使用鏈表循環(huán)確定對象),Python為對象和值分配內(nèi)存的方式常常不同于Ruby。

假設(shè)我們創(chuàng)建一個Node對象使用Python:

如何進行對比Ruby和Python的垃圾回收

Python不同于Ruby,當你創(chuàng)建對象的時候,Python會立即向操作系統(tǒng)申請分配內(nèi)存。(Python 事實上實現(xiàn)了自己的內(nèi)存分配系統(tǒng),它在操作系統(tǒng)內(nèi)存堆上提供了另外一層抽象,但是今天沒有事件深入探討。 )

當我們創(chuàng)建第二個對象時,Python將再次向操作系統(tǒng)申請更多的內(nèi)存:

如何進行對比Ruby和Python的垃圾回收

看起來相當簡單,當我們創(chuàng)建Python對象的時刻,將花費事件申請內(nèi)存。

如何進行對比Ruby和Python的垃圾回收

Ruby將沒有用的對象扔的到處都是,直到下一個垃圾回收過程

Ruby開發(fā)者生活在一個臟亂的房間

回到Ruby,由于我們分配越來越多的對象,Ruby將繼續(xù)為我們從空閑對象鏈表(free list)獲取預(yù)分配對象。因此,空閑對象鏈表將變得越來越短:

如何進行對比Ruby和Python的垃圾回收

或者更短:

如何進行對比Ruby和Python的垃圾回收

請注意,我將一個新的值賦給了n1,Ruby會遺留下舊的值?!盇BC”, “JKL”和”MNO”等結(jié)點對象會依然保留在內(nèi)存中。Ruby不會立即清理舊的對象盡管程序不再使用!作為一名Ruby開發(fā)者就像生活在一個臟亂的房間,衣服隨意的仍在地板上,廚房的水槽中堆滿了臟盤子。作為一個Ruby開發(fā)者,你必須在一大堆垃圾對象中去工作。

如何進行對比Ruby和Python的垃圾回收

當你的程序不在使用任何對象的時候,Python會立刻進行清理。

Python開發(fā)者生活在一所整潔的房子

垃圾回收機制在Python和Ruby中迥然不同,讓我們回到前面三個Python中Node對象的例子:

如何進行對比Ruby和Python的垃圾回收

內(nèi)部的,每當我們新建一個對象,Python將在對象對應(yīng)的C語言結(jié)構(gòu)中保存一個數(shù)字,叫做引用技術(shù)。最初,Python將它的值設(shè)為1。

如何進行對比Ruby和Python的垃圾回收

值為1表明每個對象有一個指針或引用指向它。假設(shè)我們創(chuàng)建一個新的對象,JKL:

如何進行對比Ruby和Python的垃圾回收

正如前面所說,Python將”JKL”的引用計數(shù)設(shè)置為1。同樣注意到我們改變n1指向了”JKL”,不再引用”ABC”,同時將”ABC”的引用計數(shù)減少為0。

通過這一點,Python垃圾回收器將會立即執(zhí)行!無論何時,只要一個對象的引用計數(shù)變?yōu)?,python將立即釋放這個對象,并且將它的內(nèi)存返回給操作系統(tǒng)。

如何進行對比Ruby和Python的垃圾回收

上圖中,Python將回收”ABC”對象的內(nèi)存。記住,Ruby只是將舊的對象遺留在那里并且不去釋放它們占用的內(nèi)存。

這種垃圾回收算法被稱為”引用計數(shù)”,由喬治柯林斯發(fā)明于1960年。非常巧合的是在同一年約翰麥卡錫大叔發(fā)明了”空閑對象鏈表算法”。正如Mike Bernstein在Ruby Conference大會上所說”1960年是屬于垃圾回收器的…”。

作為一個Python開發(fā)者,就像生活在一個整潔的房間中。你知道,你的室友有些潔癖,他會把你使用過的任何東西都清洗一遍。你把臟盤子,臟杯子一放到水槽中他就會清洗。

現(xiàn)在看另外一個例子,假設(shè)我們讓n2和n1指向同樣的結(jié)點:

如何進行對比Ruby和Python的垃圾回收

上圖左邊可以看到,Python減少了”DEF”的引用計數(shù)并且立即回收了”DEF”對象。同時可以看到,由于n1和n2同時指了”JKL”對象,所以它的引用計數(shù)變?yōu)榱?。

標記回收算法

最終臟亂的房間將堆慢垃圾,生活不能總是如此。Ruby程序在運行一段時間之后,空閑對象鏈表最終將被用盡。

如何進行對比Ruby和Python的垃圾回收

上圖中所有的預(yù)分配對象都被用盡(方塊全部變成了灰色),鏈表上沒有對象可用(沒有剩余的白色方塊)。

此時,Ruby使用了一種由約翰麥卡錫發(fā)明的被稱為”標記回收”的算法。首先,Ruby將停止程序的執(zhí)行,Ruby使用了”停止這個世界,然后回收垃圾”的方式。然后,Ruby會掃描所有的指向?qū)ο蠛椭档闹羔樆蛞谩M瑯?,Ruby也會迭代虛擬機內(nèi)部使用的指針。它會標記每一個指針所能到達的對象。在下圖中,我使用了”M”指出了這些標記:

如何進行對比Ruby和Python的垃圾回收

上面三個”M”標記的對象為活躍對象,依然被我們的程序使用。在Ruby解釋器內(nèi)部,通常使用”free bitmap”的數(shù)據(jù)結(jié)構(gòu)來保存一個對象是否被標記:

如何進行對比Ruby和Python的垃圾回收

Ruby將”free bitmap”保存在一個獨立的內(nèi)存區(qū)域,以便可以更好的利用Unix的”copy-on-write”特性。更詳細的信息,請參考我的另一篇文章《為什么Ruby2.0的垃圾回收器讓我們?nèi)绱伺d奮》。

如果活躍對象被標記了,那么其余的便是垃圾對象,意味著它們不再會被代碼使用。在下圖中,我使用白色的方塊表示垃圾對象:

如何進行對比Ruby和Python的垃圾回收

接下來,Ruby將清理沒有使用的,垃圾對象,將它們鏈入空閑對象鏈表(free list):

如何進行對比Ruby和Python的垃圾回收

在解釋器內(nèi)部,這個過程非常迅速,Ruby并不會真正的將對象從一個地方拷貝到另一個地方。相反的,Ruby會將垃圾對象組成一個新的鏈表,并且鏈入空閑對象鏈表(free list)。

現(xiàn)在,當我們要創(chuàng)建一個新的Ruby對象的時候,Ruby將為我們返回收集的垃圾對象。在Ruby中,對象是可以重生的,享受著多次的生命!

標記回收算法 vs. 引用計數(shù)算法

咋一看,Python的垃圾回收算法對于Ruby來說是相當讓人感到驚訝的:既然可以生活在一個整潔干凈的房間,為什么要生活在一個臟亂的房間呢?為什么Ruby周期性的強制停止程序的運行去清理垃圾,而不使用Python的算法呢?

然而,引用計數(shù)實現(xiàn)起來不會像它看起來那樣簡單。這里有一些許多語言不愿像Python一樣使用引用計數(shù)算法的原因:

  • 首先,實現(xiàn)起來很困難。Python必須為每一個對象留有一定的空間來保存引用計數(shù)。這會導致一些細微的內(nèi)存開銷。但更遭的是,一個簡答的操作例如改變一個變量或引用將導致復(fù)雜的操作,由于Python需要增加一個對象的計數(shù),減少另一個對象的計數(shù),有可能釋放一個對象。

  • 其次,它會減慢速度。盡管Python在程序運行過程中垃圾回收的過程非常順暢(當你把臟盤子放到水槽后,它立馬清洗干凈),但是運行的并不十分迅速。Python總是在更新引用計數(shù)。并且當你停止使用一個巨大的數(shù)據(jù)結(jié)構(gòu)時,例如一個包含了大量元素的序列,Python必須一次釋放許多對象。減少引用計數(shù)可能是一個復(fù)雜的,遞歸的過程。

  • ***,它并不總是工作的很好。在我演講的下一部分,也就是下一篇帖子中能看到,引用計數(shù)不能處理循環(huán)引用數(shù)據(jù)結(jié)構(gòu),它包含循環(huán)引用。

上述內(nèi)容就是如何進行對比Ruby和Python的垃圾回收,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI