引用計數和垃圾回收機制:http://www.cnblogs.com/vamei/p/3232088.htmlhtml
內存池機制:http://developer.51cto.com/art/201007/213585.htmpython
python的內存管理機制主要包括引用計數機制、垃圾回收機制、內存池機制。緩存
引用計數機制:函數
對於常見的賦值語句,如a=1。整數1爲對象,而a是一個引用。利用賦值語句,引用a指向對象1.可使用內置函數id()返回對象的內存地址。在python中,整數和短小的字符,python都會緩存這些對象,以便重複使用。長的字符串和其餘對象能夠有多個相同的對象,可使用賦值語句建立出新的對象。當咱們建立多個等於1的引用時,其實是讓全部這些引用指向同一個對象。爲了檢驗兩個引用指向同一個對象,咱們能夠用is關鍵字。is用於判斷兩個引用所指向的對象是否相同。
spa
在python中,每一個對象都有存有指向該對象的引用總數,及引用計數。咱們可使用sys包中的getrefcount()來查看某個對象的引用計數。須要注意的是,當使用某個引用做爲參數傳遞給getrefcount()時,參數實際上建立了一個臨時的引用。所以,getrefcount()所獲得的結果,會比指望的多1.code
python的一個容器對象,好比列表,詞典等,能夠包括多個對象。實際上,容器對象中包含的並非元素對象自己,是指向各個元素對象的引用。orm
某個對象的引用計數可能減小。好比,可使用del關鍵字刪除某個引用:>>>a=[1,2,3] >>>b=a >>>del ahtm
del也能夠用於刪除容器元素中的元素,好比:del a[0]
對象
若是某個引用指向對象A,當這個引用被從新定向到某個其餘對象B時,對象A的引用計數減小:blog
>>>a=[1,2,3] >>>b=a >>>sys.getrefcount(b) >>>a=1 >>>getrefcount(b)
垃圾回收機制:
當python中的對象愈來愈多,它們將佔據愈來愈大的內存,python會啓動垃圾回收機制。從基本原理上,當python的某個對象的引用計數降爲0時,說明沒有任何引用指向該對象,該對象就成爲要被回收的垃圾了。例如:
>>>a=[1,2,3]
>>>del a
del a後,已經沒有任何引用指向以前創建的[1,2,3]這個表。用戶不可能經過任何方式接觸或者動用這個對象。當垃圾回收啓動時,python掃描到這個引用計數爲0的對象,就將它所佔據的內存清空。
垃圾回收時,Python不能進行其它的任務。頻繁的垃圾回收將大大下降Python的工做效率。若是內存中的對象很少,就沒有必要總啓動垃圾回收。因此,Python只會在特定條件下,自動啓動垃圾回收。當Python運行時,會記錄其中分配對象(object allocation)和取消分配對象(object deallocation)的次數。當二者的差值高於某個閾值時,垃圾回收纔會啓動。
咱們能夠經過gc模塊的get_threshold()方法,查看該閾值:
import gc print(gc.get_threshold())
返回(700, 10, 10),後面的兩個10是與分代回收相關的閾值,後面能夠看到。700便是垃圾回收啓動的閾值。能夠經過gc中的set_threshold()方法從新設置。咱們也能夠手動啓動垃圾回收,即便用gc.collect()。
Python同時採用了分代(generation)回收的策略。這一策略的基本假設是,存活時間越久的對象,越不可能在後面的程序中變成垃圾。咱們的程序每每會產生大量的對象,許多對象很快產生和消失,但也有一些對象長期被使用。出於信任和效率,對於這樣一些「長壽」對象,咱們相信它們的用處,因此減小在垃圾回收中掃描它們的頻率。
Python將全部的對象分爲0,1,2三代。全部的新建對象都是0代對象。當某一代對象經歷過垃圾回收,依然存活,那麼它就被納入下一代對象。垃圾回收啓動時,必定會掃描全部的0代對象。若是0代通過必定次數垃圾回收,那麼就啓動對0代和1代的掃描清理。當1代也經歷了必定次數的垃圾回收後,那麼會啓動對0,1,2,即對全部對象進行掃描。
這兩個次數即上面get_threshold()返回的(700, 10, 10)返回的兩個10。也就是說,每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,纔會有1次的2代垃圾回收。
一樣能夠用set_threshold()來調整,好比對2代對象進行更頻繁的掃描。
import gc gc.set_threshold(700, 10, 5)
孤立的引用環:
引用環的存在會給上面的垃圾回收機制帶來很大的困難。這些引用環可能構成沒法使用,但引用計數不爲0的一些對象。爲了回收這樣的引用環,Python複製每一個對象的引用計數,能夠記爲gc_ref。假設,每一個對象i,該計數爲gc_ref_i。Python會遍歷全部的對象i。對於每一個對象i引用的對象j,將相應的gc_ref_j減1。在結束遍歷後,gc_ref不爲0的對象,和這些對象引用的對象,以及繼續更下游引用的對象,須要被保留。而其它的對象則被垃圾回收。
內存池機制:
在Python中,許多時候申請的內存都是小塊的內存,這些小塊內存在申請後,很快又會被釋放,因爲這些內存的申請並非爲了建立對象,因此並無對象一級的內存池機制。
這就意味着Python在運行期間會大量地執行malloc和free的操做,頻繁地在用戶態和核心態之間進行切換,這將嚴重影響Python的執行效率。爲了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。這也就是以前提到的Pymalloc機制。
在Python 2.5中,Python內部默認的小塊內存與大塊內存的分界點定在256個字節,這個分界點由前面咱們看到的名爲SMALL_REQUEST_THRESHOLD的符號控制。
也就是說,當申請的內存小於256字節時,PyObject_Malloc會在內存池中申請內存;當申請的內存大於256字節時,PyObject_Malloc的行爲將蛻化爲malloc的行爲。固然,經過修改Python源代碼,咱們能夠改變這個默認值,從而改變Python的默認內存管理行爲。
在一個對象的引用計數減爲0時,與該對象對應的析構函數就會被調用。
可是要特別注意的是,調用析構函數並不意味着最終必定會調用free釋放內存空間,若是真是這樣的話,那頻繁地申請、釋放內存空間會使 Python的執行效率大打折扣(更況且Python已經多年揹負了人們對其執行效率的不滿)。通常來講,Python中大量採用了內存對象池的技術,使用這種技術能夠避免頻繁地申請和釋放內存空間。所以在析構時,一般都是將對象佔用的空間歸還到內存池中。
"這個問題就是:Python的arena歷來不釋放pool。這個問題爲何會引發相似於內存泄漏的現象呢。考慮這樣一種情形,申請10*1024*1024個16字節的小內存,這就意味着必須使用160M的內存,因爲Python沒有默認將前面提到的限制內存池的WITH_MEMORY_LIMITS編譯符號打開,因此Python會徹底使用arena來知足你的需求,這都沒有問題,關鍵的問題在於過了一段時間,你將全部這些16字節的內存都釋放了,這些內存都回到arena的控制中,彷佛沒有問題。
可是問題偏偏就在這時出現了。由於arena始終不會釋放它維護的pool集合,因此這160M的內存始終被Python佔用,若是之後程序運行中不再須要160M如此巨大的內存,這點內存豈不是就浪費了?"
Python內存管理規則:del的時候,把list的元素釋放掉,把管理元素的大對象回收到py對象緩衝池裏。