1. 程式人生 > >python 記憶體管理

python 記憶體管理

#本篇內容參考了其他博主的內容,根據自己的理解做了一些整理,感謝

python記憶體管理以引入計數為主,標記清除、垃圾回收為輔。
Python記憶體管理機制分為:
引入計數
垃圾回收
記憶體池機制

變數引用物件:

注意:變數名沒有型別,型別屬於物件(因為變數引用物件,所以型別隨物件),變數引用什麼型別的物件,變數就是什麼型別的。

 

Python使用引用計數和垃圾回收來釋放(free)python物件。

可以通過is來判斷兩個引用所指的物件是否相同

由上面的執行結果可知:
1、Python快取了整數和短字串,因此每個物件在記憶體中只存有一份,引用所指物件就是相同的,即使使用賦值語句,也只是創造新的引用,而不是物件本身;
2、Python沒有快取長字串、列表及其他物件,可以有多個相同的物件,可以使用賦值語句創建出新的物件。

 

引用計數
引用計數是指:Python中每個物件都有指向該物件的引用計數,來保持追蹤記憶體中的物件。
使用引用計數的優點是原理簡單,將消耗均攤到執行時,缺點是無法處理迴圈引用。
Python原始碼中,通過Py_INCREF和Py_DECREF兩個巨集來管理物件的引用計數
檢視物件的引用計數:sys.getrefcount(xxx)
import sys
a = [1,2,3]
sys.getrefcount(a)
輸出結果:2
b = a
sys.getrefcount(b)
輸出結果:3
注意:當使用某個引用作為引數,傳遞給getrefcount()時,引數實際上建立了一個臨時的引用。因此,getrefcount()所得到的結果,會比期望的多1。

 

垃圾回收
當Python中的物件越來越多,佔據越來越大的記憶體,啟動垃圾回收(garbage collection),將沒用的物件清除。
垃圾回收用於處理迴圈引用,但是無法處理迴圈引用中定義了__del__的情況,而且每次回收會造成一定的卡頓。
gc module是python垃圾回收機制的介面模組,可以通過該module啟動/停止垃圾回收、調整回收觸發的閾值、設定除錯選項
當Python的某個物件的引用計數降為0時,說明沒有任何引用指向該物件,該物件就成為要被回收的垃圾。比如某個新建物件,被分配給某個引用,物件的引用計數變為1。如果引用被刪除(使用del刪除),物件的引用計數為0,那麼該物件就可以被垃圾回收。
如新建: a = [1,2,3]
del a #刪除a,
刪除a之後,已經沒有任何物件指向剛才新建的[1,2,3],該表引用計數變為0,使用者不可能通過任何方式接觸或者動用這個物件,當垃圾回收啟動時,Python掃描到這個引用計數為0的物件,就將它所佔據的記憶體清空。

需要注意的是:
1、垃圾回收時,Python不能進行其它的任務,頻繁的垃圾回收將大大降低Python的工作效率;
2、Python只會在特定條件下,自動啟動垃圾回收(垃圾物件少就沒必要回收)
3、當Python執行時,會記錄其中分配物件(object allocation)和取消分配物件(object deallocation)的次數。當兩者的差值高於某個閾值時,垃圾回收才會啟動。

可以通過gc模組中的get_threashold來檢視閾值
In [93]: import gc
In [94]: gc.get_threshold()#gc模組中檢視閾值的方法
Out[94]: (700, 10, 10)
700即是垃圾回收啟動的閾值;
每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,才會有1次的2代垃圾回收;
也可以手動啟動垃圾回收:
In [95]: gc.collect()
Out[94]: 2

分代回收:
Python將所有的物件根據“生存時間”分為0,1,2三代;
所有的新建物件都是0代物件;
當某一代物件經歷過垃圾回收,依然存活,就被歸入下一代物件,對於不同代的物件,python的回收頻率不一樣。當Python的垃圾回收器中新增的物件數量減去刪除的物件數量大於threshold0時, Python會對第0代物件 執行一次垃圾回收. 每當第0代被檢查的次數超過了threshold1時, 第1代物件就會被執行一次垃圾回收.,同理第2代也是。
觸發垃圾回收的情況:
(1)達到了垃圾回收的閾值,Python虛擬機器自動執行
(2)手動呼叫gc.collect()
(3)Python虛擬機器退出的時候

 

記憶體池機制
Python中有分為大記憶體和小記憶體:(256K為界線,分大小記憶體)
1)大記憶體使用malloc進行分配
2)小記憶體使用記憶體池進行分配
3)python中記憶體池--呈金字塔形
第3層:最上層,使用者對Python物件的直接操作
第1層和第2層:記憶體池,由Python的介面函式PyMem_Malloc實現---若請求分配的記憶體在1~256位元組之間就使用記憶體池管理系統進行分配,呼叫malloc函式分配記憶體,但是每次只會分配一塊大小為256K的大塊記憶體,不會呼叫free函式釋放記憶體,將該記憶體塊留在記憶體池中以便下次使用。
第0層:大記憶體-----若請求分配的記憶體大於256K,malloc函式分配記憶體,free函式釋放記憶體。
第-1,-2層:作業系統進行操作