日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
詳解python中的垃圾回收機制

python采用的是引用計數(shù)機制為主,標記-清除和**分代收集(隔代回收)**兩種機制為輔的策略,下面為大家詳細講解一下python中的垃圾回收機制。

計數(shù)引用

因為python中一切皆為對象,你所看到的一切變量,本質(zhì)上都是對象的一個指針。當一個對象不再調(diào)用的時候,也就是當這個對象的引用計數(shù)(指針數(shù))為 0 的時候,說明這個對象永不可達,自然它也就成為了垃圾,需要被回收??梢院唵蔚睦斫鉃闆]有任何變量再指向它。

import os  
import psutil  

# 顯示當前 python 程序占用的內(nèi)存大小

def show_memory_info(hint):  
   pid = os.getpid()  
   p = psutil.Process(pid)  
   info = p.memory_full_info()  
   memory = info.uss / 1024./ 1024  
print(  {} memory used: {} MB .format(hint, memory))

可以看到調(diào)用函數(shù) func(),在列表 a 被創(chuàng)建之后,內(nèi)存占用迅速增加到了 433 MB:而在函數(shù)調(diào)用結束后,內(nèi)存則返回正常。這是因為,函數(shù)內(nèi)部聲明的列表 a 是局部變量,在函數(shù)返回后,局部變量的引用會注銷掉;此時,列表 a 所指代對象的引用數(shù)為 0,Python 便會執(zhí)行垃圾回收,因此之前占用的大量內(nèi)存就又回來了。

def func():  
   show_memory_info(
initial  
)  
global a
   a = [i for  i in  range( 10000000 )]  
   show_memory_info( after a created )
func()  
show_memory_info(
finished  
)
########## 輸出 ##########  
initial memory used: 48.88671875 MB  
after a created memory used:433.94921875 MB  
finished memory used:433.94921875 MB

新的這段代碼中,global a 表示將 a 聲明為全局變量。那么,即使函數(shù)返回后,列表的引用依然存在,于是對象就不會被垃圾回收掉,依然占用大量內(nèi)存。同樣,如果我們把生成的列表返回,然后在主程序中接收,那么引用依然存在,垃圾回收就不會被觸發(fā),大量內(nèi)存仍然被占用著:

def func():  
   show_memory_info(  initial )  
   a = [i for  i in  derange( 10000000 )]  
   show_memory_info(  after a created )

return a  
a = func()
show_memory_info( finished)

########## 輸出 ##########  
initial memory used:  47.96484375 MB
after a created memory used:434.515625 MB
finished memory used: 434.515625 MB

那怎么可以看到變量被引用了多少次呢?通過 sys.getrefcount

import sys  
a = []  
# 兩次引用,一次來自 a,一次來自 getrefcount
print (sys.getrefcount(a))  

def func(a):  
# 四次引用,a,python 的函數(shù)調(diào)用棧,函數(shù)參數(shù),和 getrefcount  
print (sys.getrefcount(a))  
func(a)  
# 兩次引用,一次來自 a,一次來自 getrefcount,函數(shù) func 調(diào)用已經(jīng)不存在  
print (sys.getrefcount(a))  
########## 輸出 ##########  
2  
4  
2

如果其中涉及函數(shù)調(diào)用,會額外增加兩次1. 函數(shù)棧2. 函數(shù)調(diào)用

從這里就可以看到python不再需要像C那種的認為的釋放內(nèi)存,但是python同樣給我們提供了手動釋放內(nèi)存的方法 gc.collect()

import gc  
show_memory_info( initial)  
a = [i for  i in range( 10000000 )]  
show_memory_info(  after a created)
del a
gc.collect()
show_memory_info( finish )  
print (a)  
########## 輸出 ##########
initial memory used: 48.1015625 MB
after a created memory used: 434.3828125 MB  
finish memory used: 48.33203125 MB
---------------------------------------------------------------------------
NameErrorTraceback (most recent call last)

in  
11  
12 show_memory_info(  finish )
--->  13 print (a)

NameError : name  a  isnotdefined

截止目前,貌似python的垃圾回收機制非常的簡單,只要對象引用次數(shù)為0,必定為觸發(fā)gc,那么引用次數(shù)為0是否是觸發(fā)gc的充要條件呢?

循環(huán)回收

如果有兩個對象,它們互相引用,并且不再被別的對象所引用,那么它們應該被垃圾回收嗎?

def func():
   show_memory_info( initial )  
   a = [i for  i in  range(10000000)]
   b = [i for  i in  range(10000000)]  
   show_memory_info(  after a, b created )  
   a.append(b)  
   b.append(a)
func()
show_memory_info(  finished )  
########## 輸出 ##########  
initial memory used: 47.984375 MB  
after a, b created memory used:822.73828125 MB  
finished memory used:  821.73046875 MB

從結果顯而易見,它們并沒有被回收,但是從程序上來看,當這個函數(shù)結束的時候,作為局部變量的a,b就已經(jīng)從程序意義上不存在了。但是因為它們的互相引用,導致了它們的引用數(shù)都不為0。這時要如何規(guī)避呢

\1. 從代碼邏輯上進行整改,避免這種循環(huán)引用

\2. 通過人工回收

import gc
def func():  
   show_memory_info( initial)  
   a = [i for  i in  range(10000000)]  
   b = [i for  i in  range(10000000)]
   show_memory_info( after a, b created)  
   a.append(b)
   b.append(a)
func()
gc.collect()
show_memory_info( finished )  
########## 輸出 ##########  
initial memory used:49.51171875 MB  
after a, b created memory used: 824.1328125 MB  
finished memory used:49.98046875 MB

python針對循環(huán)引用,有它的自動垃圾回收算法1. 標記清除(mark-sweep)算法2. 分代收集(generational)

標記清除

標記清除的步驟總結為如下步驟1. GC會把所有的『活動對象』打上標記2. 把那些沒有標記的對象『非活動對象』進行回收那么python如何判斷何為非活動對象?通過用圖論來理解不可達的概念。對于一個有向圖,如果從一個節(jié)點出發(fā)進行遍歷,并標記其經(jīng)過的所有節(jié)點;那么,在遍歷結束后,所有沒有被標記的節(jié)點,我們就稱之為不可達節(jié)點。顯而易見,這些節(jié)點的存在是沒有任何意義的,自然的,我們就需要對它們進行垃圾回收。但是每次都遍歷全圖,對于 Python 而言是一種巨大的性能浪費。所以,在 Python 的垃圾回收實現(xiàn)中,mark-sweep 使用雙向鏈表維護了一個數(shù)據(jù)結構,并且只考慮容器類的對象(只有容器類對象,list、dict、tuple,instance,才有可能產(chǎn)生循環(huán)引用)。

Python之“垃圾”回收Python之“垃圾”回收

圖中把小黑圈視為全局變量,也就是把它作為root object,從小黑圈出發(fā),對象1可直達,那么它將被標記,對象2、3可間接到達也會被標記,而4和5不可達,那么1、2、3就是活動對象,4和5是非活動對象會被GC回收。

分代回收

分代回收是一種以空間換時間的操作方式,Python將內(nèi)存根據(jù)對象的存活時間劃分為不同的集合,每個集合稱為一個代,Python將內(nèi)存分為了3“代”,分別為年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。新創(chuàng)建的對象都會分配在年輕代,年輕代鏈表的總數(shù)達到上限時(當垃圾回收器中新增對象減去刪除對象達到相應的閾值時),Python垃圾收集機制就會被觸發(fā),把那些可以被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推,老年代中的對象是存活時間最久的對象,甚至是存活于整個系統(tǒng)的生命周期內(nèi)。同時,分代回收是建立在標記清除技術基礎之上。事實上,分代回收基于的思想是,新生的對象更有可能被垃圾回收,而存活更久的對象也有更高的概率繼續(xù)存活。因此,通過這種做法,可以節(jié)約不少計算量,從而提高 Python 的性能。所以對于剛剛的問題,引用計數(shù)只是觸發(fā)gc的一個充分非必要條件,循環(huán)引用同樣也會觸發(fā)。

調(diào)試

可以使用 objgraph來調(diào)試程序,因為目前它的官方文檔,還沒有細讀,只能把文檔放在這供大家參閱啦~其中兩個函數(shù)非常有用 1. show_refs() 2. show_backrefs()


標題名稱:詳解python中的垃圾回收機制
轉載源于:http://www.dlmjj.cn/article/djhgejd.html