黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

Python的垃圾回收機制深入分析

系統(tǒng) 2159 0

一、概述:

Python的GC模塊主要運用了“引用計數(shù)”(reference counting)來跟蹤和回收垃圾 。 在引用計數(shù)的基礎上,還可以通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環(huán)引用的問題 。 通過“分代回收”(generation collection)以空間換取時間來進一步提高垃圾回收的效率 。

二、引用計數(shù)

在Python中,大多數(shù)對象的生命周期都是通過對象的引用計數(shù)來管理的。從廣義上來講,引用計數(shù)也是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術。

原理:當一個對象的引用被創(chuàng)建或者復制時,對象的引用計數(shù)加1;當一個對象的引用被銷毀時,對象的引用計數(shù)減1;當對象的引用計數(shù)減少為0時,就意味著對象已經(jīng)沒有被任何人使用了,可以將其所占用的內存釋放了。
雖然引用計數(shù)必須在每次分配和釋放內存的時候加入管理引用計數(shù)的動作,然而與其他主流的垃圾收集技術相比,引用計數(shù)有一個最大的有點,即“實時性”,任何內存,一旦沒有指向它的引用,就會立即被回收。而其他的垃圾收集計數(shù)必須在某種特殊條件下(比如內存分配失敗)才能進行無效內存的回收。

引用計數(shù)機制執(zhí)行效率問題:引用計數(shù)機制所帶來的維護引用計數(shù)的額外操作與Python運行中所進行的內存分配和釋放,引用賦值的次數(shù)是成正比的。而這點相比其他主流的垃圾回收機制,比如“標記-清除”,“停止-復制”,是一個弱點,因為這些技術所帶來的額外操作基本上只是與待回收的內存數(shù)量有關。
如果說執(zhí)行效率還僅僅是引用計數(shù)機制的一個軟肋的話,那么很不幸,引用計數(shù)機制還存在著一個致命的弱點,正是由于這個弱點,使得俠義的垃圾收集從來沒有將引用計數(shù)包含在內,能引發(fā)出這個致命的弱點就是循環(huán)引用(也稱交叉引用)。

問題說明:

循環(huán)引用可以使一組對象的引用計數(shù)不為0,然而這些對象實際上并沒有被任何外部對象所引用,它們之間只是相互引用。這意味著不會再有人使用這組對象,應該回收這組對象所占用的內存空間,然后由于相互引用的存在,每一個對象的引用計數(shù)都不為0,因此這些對象所占用的內存永遠不會被釋放。比如:

            
a = []
b = []
a.append(b)
b.append(a)
print a
[[[…]]]
print b
[[[…]]]

          

這一點是致命的,這與手動進行內存管理所產生的內存泄露毫無區(qū)別。
要解決這個問題,Python引入了其他的垃圾收集機制來彌補引用計數(shù)的缺陷:“標記-清除”,“分代回收”兩種收集技術。

三、標記-清除

“標記-清除”是為了解決循環(huán)引用的問題??梢园渌麑ο笠玫娜萜鲗ο螅ū热纾簂ist,set,dict,class,instance)都可能產生循環(huán)引用。
我們必須承認一個事實,如果兩個對象的引用計數(shù)都為1,但是僅僅存在他們之間的循環(huán)引用,那么這兩個對象都是需要被回收的,也就是說,它們的引用計數(shù)雖然表現(xiàn)為非0,但實際上有效的引用計數(shù)為0。我們必須先將循環(huán)引用摘掉,那么這兩個對象的有效計數(shù)就現(xiàn)身了。假設兩個對象為A、B,我們從A出發(fā),因為它有一個對B的引用,則將B的引用計數(shù)減1;然后順著引用達到B,因為B有一個對A的引用,同樣將A的引用減1,這樣,就完成了循環(huán)引用對象間環(huán)摘除。
但是這樣就有一個問題,假設對象A有一個對象引用C,而C沒有引用A,如果將C計數(shù)引用減1,而最后A并沒有被回收,顯然,我們錯誤的將C的引用計數(shù)減1,這將導致在未來的某個時刻出現(xiàn)一個對C的懸空引用。這就要求我們必須在A沒有被刪除的情況下復原C的引用計數(shù),如果采用這樣的方案,那么維護引用計數(shù)的復雜度將成倍增加。

原理:“標記-清除”采用了更好的做法,我們并不改動真實的引用計數(shù),而是將集合中對象的引用計數(shù)復制一份副本,改動該對象引用的副本。對于副本做任何的改動,都不會影響到對象生命走起的維護。
這個計數(shù)副本的唯一作用是尋找root object集合(該集合中的對象是不能被回收的)。當成功尋找到root object集合之后,首先將現(xiàn)在的內存鏈表一分為二,一條鏈表中維護root object集合,成為root鏈表,而另外一條鏈表中維護剩下的對象,成為unreachable鏈表。之所以要剖成兩個鏈表,是基于這樣的一種考慮:現(xiàn)在的unreachable可能存在被root鏈表中的對象,直接或間接引用的對象,這些對象是不能被回收的,一旦在標記的過程中,發(fā)現(xiàn)這樣的對象,就將其從unreachable鏈表中移到root鏈表中;當完成標記后,unreachable鏈表中剩下的所有對象就是名副其實的垃圾對象了,接下來的垃圾回收只需限制在unreachable鏈表中即可。

四、分代回收

背景:分代的垃圾收集技術是在上個世紀80年代初發(fā)展起來的一種垃圾收集機制,一系列的研究表明: 無論使用何種語言開發(fā),無論開發(fā)的是何種類型,何種規(guī)模的程序,都存在這樣一點相同之處。即:一定比例的內存塊的生存周期都比較短,通常是幾百萬條機器指令的時間,而剩下的內存塊,起生存周期比較長,甚至會從程序開始一直持續(xù)到程序結束
從前面“標記-清除”這樣的垃圾收集機制來看, 這種垃圾收集機制所帶來的額外操作實際上與系統(tǒng)中總的內存塊的數(shù)量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作 。為了提高垃圾收集的效率,采用“空間換時間的策略”。

原理:將系統(tǒng)中的所有內存塊根據(jù)其存活時間劃分為不同的集合,每一個集合就成為一個“代”,垃圾收集的頻率隨著“代”的存活時間的增大而減小。也就是說,活得越長的對象,就越不可能是垃圾,就應該減少對它的垃圾收集頻率。那么如何來衡量這個存活時間:通常是利用幾次垃圾收集動作來衡量,如果一個對象經(jīng)過的垃圾收集次數(shù)越多,可以得出:該對象存活時間就越長。

舉例說明:

當某些內存塊M經(jīng)過了3次垃圾收集的清洗之后還存活時,我們就將內存塊M劃到一個集合A中去,而新分配的內存都劃分到集合B中去。當垃圾收集開始工作時,大多數(shù)情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間后才進行,這就使得垃圾收集機制需要處理的內存少了,效率自然就提高了。在這個過程中,集合B中的某些內存塊由于存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。
在Python中,總共有3“代”,也就是Python實際上維護了3條鏈表。具體可以查看Python源碼詳細了解。


更多文章、技術交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發(fā)表我的評論
最新評論 總共0條評論