日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python的垃圾处理机制_Python语法入门之垃圾回收机制

發布時間:2025/3/20 python 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python的垃圾处理机制_Python语法入门之垃圾回收机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一 引入

解釋器在執行到定義變量的語法時,會申請內存空間來存放變量的值,而內存的容量是有限的,這就涉及到變量值所占用內存空間的回收問題,當一個變量值沒有用了(簡稱垃圾)就應該將其占用的內存給回收掉,那什么樣的變量值是沒有用的呢?

單從邏輯層面分析,我們定義變量將變量值存起來的目的是為了以后取出來使用,而取得變量值需要通過其綁定的直接引用(如x=10,10被x直接引用)或間接引用(如l=[x,],x=10,10被x直接引用,而被容器類型l間接引用),所以當一個變量值不再綁定任何引用時,我們就無法再訪問到該變量值了,該變量值自然就是沒有用的,就應該被當成一個垃圾回收。

毫無疑問,內存空間的申請與回收都是非常耗費精力的事情,而且存在很大的危險性,稍有不慎就有可能引發內存溢出問題,好在Cpython解釋器提供了自動的垃圾回收機制來幫我們解決了這件事。

二 什么是垃圾回收機制?

垃圾回收機制(簡稱GC)是Python解釋器自帶一種機,專門用來回收不可用的變量值所占用的內存空間

三 為什么要用垃圾回收機制?

程序運行過程中會申請大量的內存空間,而對于一些無用的內存空間如果不及時清理的話會導致內存使用殆盡(內存溢出),導致程序崩潰,因此管理內存是一件重要且繁雜的事情,而python解釋器自帶的垃圾回收機制把程序員從繁雜的內存管理中解放出來。

四、理解GC原理需要儲備的知識

堆區與棧區

在定義變量時,變量名與變量值都是需要存儲的,分別對應內存中的兩塊區域:堆區與棧區。

# 1、變量名與值內存地址的關聯關系存放于棧區

# 2、變量值存放于堆區,內存管理回收的則是堆區的內容,

定義了兩個變量x = 10、y = 20,詳解如下圖,

當我們執行x=y時,內存中的棧區與堆區變化如下

直接引用與間接引用

直接引用指的是從棧區出發直接引用到的內存地址。

間接引用指的是從棧區出發引用到堆區后,再通過進一步引用才能到達的內存地址。

l2 = [20, 30] # 列表本身被變量名l2直接引用,包含的元素被列表間接引用

x = 10 # 值10被變量名x直接引用

l1 = [x, l2] # 列表本身被變量名l1直接引用,包含的元素被列表間接引用

圖解如下:

五、垃圾回收機制原理分析

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

引用計數

引用計數就是:變量值被變量名關聯的次數

如:age=18

變量值18被關聯了一個變量名age,稱之為引用計數為1

引用計數增加:

age=18 (此時,變量值18的引用計數為1)

m=age (把age的內存地址給了m,此時,m,age都關聯了18,所以變量值18的引用計數為2)

引用計數減少:

age=10(名字age先與值18解除關聯,再與3建立了關聯,變量值18的引用計數為1)

del m(del的意思是解除變量名x與變量值18的關聯關系,此時,變量18的引用計數為0)

![img](data:image/svg+xml;utf8,)

值18的引用計數一旦變為0,其占用的內存地址就應該被解釋器的垃圾回收機制回收

引用計數的問題與解決方案

問題一:循環引用

引用計數機制存在著一個致命的弱點,即循環引用(也稱交叉引用)

# 如下我們定義了兩個列表,簡稱列表1與列表2,變量名l1指向列表1,變量名l2指向列表2

>>> l1=['xxx'] # 列表1被引用一次,列表1的引用計數變為1

>>> l2=['yyy'] # 列表2被引用一次,列表2的引用計數變為1

>>> l1.append(l2) # 把列表2追加到l1中作為第二個元素,列表2的引用計數變為2

>>> l2.append(l1) # 把列表1追加到l2中作為第二個元素,列表1的引用計數變為2

# l1與l2之間有相互引用

# l1 = ['xxx'的內存地址,列表2的內存地址]

# l2 = ['yyy'的內存地址,列表1的內存地址]

>>> l1

['xxx', ['yyy', [...]]]

>>> l2

['yyy', ['xxx', [...]]]

>>> l1[1][1][0]

'xxx'

循環引用會導致:值不再被任何名字關聯,但是值的引用計數并不會為0,應該被回收但不能被回收,什么意思呢?試想一下,請看如下操作

>>> del l1 # 列表1的引用計數減1,列表1的引用計數變為1

>>> del l2 # 列表2的引用計數減1,列表2的引用計數變為1

此時,只剩下列表1與列表2之間的相互引用

但此時兩個列表的引用計數均不為0,但兩個列表不再被任何其他對象關聯,沒有任何人可以再引用到它們,所以它倆占用內存空間應該被回收,但由于相互引用的存在,每一個對象的引用計數都不為0,因此這些對象所占用的內存永遠不會被釋放,所以循環引用是致命的,這與手動進行內存管理所產生的內存泄露毫無區別。 所以Python引入了“標記-清除” 與“分代回收”來分別解決引用計數的循環引用與效率低的問題

解決方案:標記-清除

容器對象(比如:list,set,dict,class,instance)都可以包含對其他對象的引用,所以都可能產生循環引用。而“標記-清除”計數就是為了解決循環引用的問題。

標記/清除算法的做法是當應用程序可用的內存空間被耗盡的時,就會停止整個程序,然后進行兩項工作,第一項則是標記,第二項則是清除

#1、標記

通俗地講就是:

棧區相當于“根”,凡是從根出發可以訪達(直接或間接引用)的,都稱之為“有根之人”,有根之人當活,無根之人當死。

具體地:標記的過程其實就是,遍歷所有的GC Roots對象(棧區中的所有內容或者線程都可以作為GC Roots對象),然后將所有GC Roots的對象可以直接或間接訪問到的對象標記為存活的對象,其余的均為非存活對象,應該被清除。

#2、清除

清除的過程將遍歷堆中所有的對象,將沒有標記的對象全部清除掉。

基于上例的循環引用,當我們同時刪除l1與l2時,會清理到棧區中l1與l2的內容以及直接引用關系

這樣在啟用標記清除算法時,從棧區出發,沒有任何一條直接或間接引用可以訪達l1與l2,即l1與l2成了“無根之人”,于是l1與l2都沒有被標記為存活,二者會被清理掉,這樣就解決了循環引用帶來的內存泄漏問題。

問題二:效率問題

基于引用計數的回收機制,每次回收內存,都需要把所有對象的引用計數都遍歷一遍,這是非常消耗時間的,于是引入了分代回收來提高回收效率,分代回收采用的是用“空間換時間”的策略。

解決方案:分代回收

分代:

分代回收的核心思想是:在歷經多次掃描的情況下,都沒有被回收的變量,gc機制就會認為,該變量是常用變量,gc對其掃描的頻率會降低,具體實現原理如下:

分代指的是根據存活時間來為變量劃分不同等級(也就是不同的代)

新定義的變量,放到新生代這個等級中,假設每隔1分鐘掃描新生代一次,如果發現變量依然被引用,那么該對象的權重(權重本質就是個整數)加一,當變量的權重大于某個設定得值(假設為3),會將它移動到更高一級的青春代,青春代的gc掃描的頻率低于新生代(掃描時間間隔更長),假設5分鐘掃描青春代一次,這樣每次gc需要掃描的變量的總個數就變少了,節省了掃描的總時間,接下來,青春代中的對象,也會以同樣的方式被移動到老年代中。也就是等級(代)越高,被垃圾回收機制掃描的頻率越低

回收:

回收依然是使用引用計數作為回收的依據

雖然分代回收可以起到提升效率的效果,但也存在一定的缺點:

#例如一個變量剛剛從新生代移入青春代,該變量的綁定關系就解除了,該變量應該被回收,但青春代的掃描頻率低于新生代,這就到導致了應該被回收的垃圾沒有得到及時地清理。

沒有十全十美的方案:

毫無疑問,如果沒有分代回收,即引用計數機制一直不停地對所有變量進行全體掃描,可以更及時地清理掉垃圾占用的內存,但這種一直不停地對所有變量進行全體掃描的方式效率極低,所以我們只能將二者中和。

綜上

垃圾回收機制是在清理垃圾&釋放內存的大背景下,允許分代回收以極小部分垃圾不會被及時釋放為代價,以此換取引用計數整體掃描頻率的降低,從而提升其性能,這是一種以空間換時間的解決方案目錄

總結

以上是生活随笔為你收集整理的python的垃圾处理机制_Python语法入门之垃圾回收机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。