Python的內(nèi)存管理機(jī)制:引入計(jì)數(shù)、垃圾回收、內(nèi)存池機(jī)制
一、變量與對(duì)象
關(guān)系圖如下:
1、變量,通過變量指針引用對(duì)象
變量指針指向具體對(duì)象的內(nèi)存空間,取對(duì)象的值。
2、對(duì)象,類型已知,每個(gè)對(duì)象都包含一個(gè)頭部信息(頭部信息:類型標(biāo)識(shí)符和引用計(jì)數(shù)器)
注意:
變量名沒有類型,類型屬于對(duì)象(因?yàn)樽兞恳脤?duì)象,所以類型隨對(duì)象),變量引用什么類型的對(duì)象,變量就是什么類型的。
In [32]: var1=object
In [33]: var2=var1
In [34]: id(var1)
Out[34]: 139697863383968In [35]: id(var2)
Out[35]: 139697863383968
PS:id()是python的內(nèi)置函數(shù),用于返回對(duì)象的身份,即對(duì)象的內(nèi)存地址。
In [39]: a=123In [40]: b=a In [41]: id(a) Out[41]: 23242832In [42]: id(b) Out[42]: 23242832In [43]: a=456In [44]: id(a) Out[44]: 33166408In [45]: id(b) Out[45]: 23242832
3、引用所指判斷
通過is進(jìn)行引用所指判斷,is是用來判斷兩個(gè)引用所指的對(duì)象是否相同。
整數(shù)
In [46]: a=1In [47]: b=1In [48]: print(a is b) True
短字符串
In [49]: c="good"In [50]: d="good"In [51]: print(c is d) True
長(zhǎng)字符串
In [52]: e="very good"In [53]: f="very good"In [54]: print(e is f) False
列表
In [55]: g=[] In [56]: h=[] In [57]: print(g is h) False
由運(yùn)行結(jié)果可知:
1、Python緩存了整數(shù)和短字符串,因此每個(gè)對(duì)象在內(nèi)存中只存有一份,引用所指對(duì)象就是相同的,即使使用賦值語(yǔ)句,也只是創(chuàng)造新的引用,而不是對(duì)象本身;
2、Python沒有緩存長(zhǎng)字符串、列表及其他對(duì)象,可以由多個(gè)相同的對(duì)象,可以使用賦值語(yǔ)句創(chuàng)建出新的對(duì)象。
在Python中,每個(gè)對(duì)象都有指向該對(duì)象的引用總數(shù)---引用計(jì)數(shù)
查看對(duì)象的引用計(jì)數(shù):sys.getrefcount()
1、普通引用
In [2]: import sys In [3]: a=[1,2,3] In [4]: getrefcount(a) Out[4]: 2In [5]: b=a In [6]: getrefcount(a) Out[6]: 3In [7]: getrefcount(b) Out[7]: 3
注意:
當(dāng)使用某個(gè)引用作為參數(shù),傳遞給getrefcount()時(shí),參數(shù)實(shí)際上創(chuàng)建了一個(gè)臨時(shí)的引用。因此,getrefcount()所得到的結(jié)果,會(huì)比期望的多1。
2、容器對(duì)象
Python的一個(gè)容器對(duì)象(比如:表、詞典等),可以包含多個(gè)對(duì)象。
In [12]: a=[1,2,3,4,5] In [13]: b=a In [14]: a is b Out[14]: True In [15]: a[0]=6 In [16]: a Out[16]: [6, 2, 3, 4, 5] In [17]: a is b Out[17]: True In [18]: b Out[18]: [6, 2, 3, 4, 5]
由上可見,實(shí)際上,容器對(duì)象中包含的并不是元素對(duì)象本身,是指向各個(gè)元素對(duì)象的引用。
3、引用計(jì)數(shù)增加
1、對(duì)象被創(chuàng)建
In [39]: getrefcount(123) Out[39]: 6In [40]: n=123In [41]: getrefcount(123) Out[41]: 7
2、另外的別人被創(chuàng)建
In [42]: m=n In [43]: getrefcount(123) Out[43]: 8
3、作為容器對(duì)象的一個(gè)元素
In [44]: a=[1,12,123] In [45]: getrefcount(123) Out[45]: 9
4、被作為參數(shù)傳遞給函數(shù):foo(x)
4、引用計(jì)數(shù)減少
1、對(duì)象的別名被顯式的銷毀
In [46]: del m In [47]: getrefcount(123) Out[47]: 8
2、對(duì)象的一個(gè)別名被賦值給其他對(duì)象
In [48]: n=456In [49]: getrefcount(123) Out[49]: 7
3、對(duì)象從一個(gè)窗口對(duì)象中移除,或,窗口對(duì)象本身被銷毀
In [50]: a.remove(123) In [51]: a Out[51]: [1, 12] In [52]: getrefcount(123) Out[52]: 6
4、一個(gè)本地引用離開了它的作用域,比如上面的foo(x)函數(shù)結(jié)束時(shí),x指向的對(duì)象引用減1。
當(dāng)Python中的對(duì)象越來越多,占據(jù)越來越大的內(nèi)存,啟動(dòng)垃圾回收(garbage collection),將沒用的對(duì)象清除。
1、原理
當(dāng)Python的某個(gè)對(duì)象的引用計(jì)數(shù)降為0時(shí),說明沒有任何引用指向該對(duì)象,該對(duì)象就成為要被回收的垃圾。比如某個(gè)新建對(duì)象,被分配給某個(gè)引用,對(duì)象的引用計(jì)數(shù)變?yōu)?。如果引用被刪除,對(duì)象的引用計(jì)數(shù)為0,那么該對(duì)象就可以被垃圾回收。
In [74]: a=[321,123] In [75]: del a
2、解析del
del a后,已經(jīng)沒有任何引用指向之前建立的[321,123],該表引用計(jì)數(shù)變?yōu)?,用戶不可能通過任何方式接觸或者動(dòng)用這個(gè)對(duì)象,當(dāng)垃圾回收啟動(dòng)時(shí),Python掃描到這個(gè)引用計(jì)數(shù)為0的對(duì)象,就將它所占據(jù)的內(nèi)存清空。
3、注意
1、垃圾回收時(shí),Python不能進(jìn)行其它的任務(wù),頻繁的垃圾回收將大大降低Python的工作效率;
2、Python只會(huì)在特定條件下,自動(dòng)啟動(dòng)垃圾回收(垃圾對(duì)象少就沒必要回收)
3、當(dāng)Python運(yùn)行時(shí),會(huì)記錄其中分配對(duì)象(object allocation)和取消分配對(duì)象(object deallocation)的次數(shù)。當(dāng)兩者的差值高于某個(gè)閾值時(shí),垃圾回收才會(huì)啟動(dòng)。
In [93]: import gc In [94]: gc.get_threshold() #gc模塊中查看閾值的方法Out[94]: (700, 10, 10)
閾值分析:
700即是垃圾回收啟動(dòng)的閾值;
每10次0代垃圾回收,會(huì)配合1次1代的垃圾回收;而每10次1代的垃圾回收,才會(huì)有1次的2代垃圾回收;
當(dāng)然也是可以手動(dòng)啟動(dòng)垃圾回收:
In [95]: gc.collect() #手動(dòng)啟動(dòng)垃圾回收Out[95]: 2
4、何為分代回收
Python將所有的對(duì)象分為0,1,2三代;
所有的新建對(duì)象都是0代對(duì)象;
當(dāng)某一代對(duì)象經(jīng)歷過垃圾回收,依然存活,就被歸入下一代對(duì)象。
Python中有分為大內(nèi)存和小內(nèi)存:(256K為界限分大小內(nèi)存)
1、大內(nèi)存使用malloc進(jìn)行分配
2、小內(nèi)存使用內(nèi)存池進(jìn)行分配
3、Python的內(nèi)存池(金字塔)
第3層:最上層,用戶對(duì)Python對(duì)象的直接操作
第1層和第2層:內(nèi)存池,有Python的接口函數(shù)PyMem_Malloc實(shí)現(xiàn)-----若請(qǐng)求分配的內(nèi)存在1~256字節(jié)之間就使用內(nèi)存池管理系統(tǒng)進(jìn)行分配,調(diào)用malloc函數(shù)分配內(nèi)存,但是每次只會(huì)分配一塊大小為256K的大塊內(nèi)存,不會(huì)調(diào)用free函數(shù)釋放內(nèi)存,將該內(nèi)存塊留在內(nèi)存池中以便下次使用。
第0層:大內(nèi)存-----若請(qǐng)求分配的內(nèi)存大于256K,malloc函數(shù)分配內(nèi)存,free函數(shù)釋放內(nèi)存。
第-1,-2層:操作系統(tǒng)進(jìn)行操作
@author:http://www.cnblogs.com/geaozhang/
標(biāo)簽: Python
http://www.cnblogs.com/geaozhang/p/7111961.html