日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
聊一聊Python中的“垃圾”回收

前言

創(chuàng)新互聯(lián)建站是一家集網(wǎng)站建設(shè),福海企業(yè)網(wǎng)站建設(shè),福海品牌網(wǎng)站建設(shè),網(wǎng)站定制,福海網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,福海網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

對(duì)于python來說,一切皆為對(duì)象,所有的變量賦值都遵循著對(duì)象引用機(jī)制。程序在運(yùn)行的時(shí)候,需要在內(nèi)存中開辟出一塊空間,用于存放運(yùn)行時(shí)產(chǎn)生的臨時(shí)變量;計(jì)算完成后,再將結(jié)果輸出到永久性存儲(chǔ)器中。如果數(shù)據(jù)量過大,內(nèi)存空間管理不善就很容易出現(xiàn) OOM(out of memory),俗稱爆內(nèi)存,程序可能被操作系統(tǒng)中止。而對(duì)于服務(wù)器,內(nèi)存管理則顯得更為重要,不然很容易引發(fā)內(nèi)存泄漏- 這里的泄漏,并不是說你的內(nèi)存出現(xiàn)了信息安全問題,被惡意程序利用了,而是指程序本身沒有設(shè)計(jì)好,導(dǎo)致程序未能釋放已不再使用的內(nèi)存。- 內(nèi)存泄漏也不是指你的內(nèi)存在物理上消失了,而是意味著代碼在分配了某段內(nèi)存后,因?yàn)樵O(shè)計(jì)錯(cuò)誤,失去了對(duì)這段內(nèi)存的控制,從而造成了內(nèi)存的浪費(fèi)。也就是這塊內(nèi)存脫離了gc的控制。

計(jì)數(shù)引用

因?yàn)閜ython中一切皆為對(duì)象,你所看到的一切變量,本質(zhì)上都是對(duì)象的一個(gè)指針。當(dāng)一個(gè)對(duì)象不再調(diào)用的時(shí)候,也就是當(dāng)這個(gè)對(duì)象的引用計(jì)數(shù)(指針數(shù))為 0 的時(shí)候,說明這個(gè)對(duì)象永不可達(dá),自然它也就成為了垃圾,需要被回收??梢院?jiǎn)單的理解為沒有任何變量再指向它。

 
 
 
 
  1. import os  
  2. import psutil   
  3.  
  4. # 顯示當(dāng)前 python 程序占用的內(nèi)存大小 
  5.  
  6. def show_memory_info(hint):  
  7.     pid = os.getpid()  
  8.     p = psutil.Process(pid)  
  9.     info = p.memory_full_info()  
  10.     memory = info.uss / 1024./ 1024  
  11. print(  {} memory used: {} MB .format(hint, memory)) 

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

 
 
 
 
  1. def func():  
  2.     show_memory_info( 
  3.  initial  
  4. )  
  5. global a 
  6. a = [i for  i in  range( 10000000 )]  
  7.     show_memory_info( after a created ) 
  8. func()  
  9. show_memory_info( 
  10.  finished  
  11. ########## 輸出 ##########  
  12. initial memory used: 48.88671875 MB  
  13. after a created memory used:433.94921875 MB  
  14. finished memory used:433.94921875 MB 

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

 
 
 
 
  1. def func():  
  2.     show_memory_info(  initial )  
  3.     a = [i for  i in  derange( 10000000 )]  
  4.     show_memory_info(  after a created ) 
  5.  
  6. return a  
  7. a = func() 
  8. show_memory_info( finished) 
  9.  
  10. ########## 輸出 ##########  
  11. initial memory used:  47.96484375 MB 
  12. after a created memory used:434.515625 MB 
  13. finished memory used: 434.515625 MB 

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

 
 
 
 
  1. import sys  
  2. a = []  
  3. # 兩次引用,一次來自 a,一次來自 getrefcount 
  4. print (sys.getrefcount(a))  
  5.  
  6. def func(a):  
  7. # 四次引用,a,python 的函數(shù)調(diào)用棧,函數(shù)參數(shù),和 getrefcount  
  8. print (sys.getrefcount(a))  
  9. func(a)  
  10. # 兩次引用,一次來自 a,一次來自 getrefcount,函數(shù) func 調(diào)用已經(jīng)不存在  
  11. print (sys.getrefcount(a))   
  12. ########## 輸出 ##########  
  13. 2  
  14. 4  

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

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

 
 
 
 
  1. import gc  
  2. show_memory_info( initial)  
  3. a = [i for  i in range( 10000000 )]  
  4. show_memory_info(  after a created) 
  5. del a 
  6. gc.collect() 
  7. show_memory_info( finish )  
  8. print (a)  
  9. ########## 輸出 ########## 
  10. initial memory used: 48.1015625 MB 
  11. after a created memory used: 434.3828125 MB  
  12. finish memory used: 48.33203125 MB 
  13. --------------------------------------------------------------------------- 
  14. NameErrorTraceback (most recent call last) 
  15.  
  16.  in  
  17. 11  
  18. 12 show_memory_info(  finish ) 
  19. --->  13 print (a) 
  20.  
  21. NameError : name  a  isnotdefined 

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

循環(huán)回收

如果有兩個(gè)對(duì)象,它們互相引用,并且不再被別的對(duì)象所引用,那么它們應(yīng)該被垃圾回收嗎?

 
 
 
 
  1. def func(): 
  2.     show_memory_info( initial )  
  3.     a = [i for  i in  range(10000000)] 
  4.     b = [i for  i in  range(10000000)]  
  5.     show_memory_info(  after a, b created )  
  6.     a.append(b)  
  7.     b.append(a) 
  8. func() 
  9. show_memory_info(  finished )  
  10. ########## 輸出 ##########  
  11. initial memory used: 47.984375 MB  
  12. after a, b created memory used:822.73828125 MB  
  13. finished memory used:  821.73046875 MB 

從結(jié)果顯而易見,它們并沒有被回收,但是從程序上來看,當(dāng)這個(gè)函數(shù)結(jié)束的時(shí)候,作為局部變量的a,b就已經(jīng)從程序意義上不存在了。但是因?yàn)樗鼈兊幕ハ嘁茫瑢?dǎo)致了它們的引用數(shù)都不為0。這時(shí)要如何規(guī)避呢1. 從代碼邏輯上進(jìn)行整改,避免這種循環(huán)引用2. 通過人工回收。

 
 
 
 
  1. import gc 
  2. def func():  
  3.     show_memory_info( initial)  
  4.     a = [i for  i in  range(10000000)]  
  5.     b = [i for  i in  range(10000000)] 
  6.     show_memory_info( after a, b created)  
  7.     a.append(b) 
  8.     b.append(a) 
  9. func() 
  10. gc.collect() 
  11. show_memory_info( finished )  
  12. ########## 輸出 ##########  
  13. initial memory used:49.51171875 MB  
  14. after a, b created memory used: 824.1328125 MB  
  15. finished memory used:49.98046875 MB 

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

標(biāo)記清除

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

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

分代回收

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

調(diào)試

可以使用 objgraph來調(diào)試程序,因?yàn)槟壳八墓俜轿臋n,還沒有細(xì)讀,只能把文檔放在這供大家參閱啦~其中兩個(gè)函數(shù)非常有用 1. show_refs() 2. show_backrefs()。


網(wǎng)站題目:聊一聊Python中的“垃圾”回收
文章出自:http://www.5511xx.com/article/cdceees.html