Python的內(nèi)存管理機(jī)制引入計(jì)數(shù)、垃圾回收、內(nèi)存池機(jī)制

一、變量與對(duì)象

關(guān)系圖如下:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

1、變量,通過變量指針引用對(duì)象

  變量指針指向具體對(duì)象的內(nèi)存空間,取對(duì)象的值。

2、對(duì)象,類型已知,每個(gè)對(duì)象都包含一個(gè)頭部信息(頭部信息:類型標(biāo)識(shí)符和引用計(jì)數(shù)器)

注意:

  變量名沒有類型,類型屬于對(duì)象(因?yàn)樽兞恳脤?duì)象,所以類型隨對(duì)象),變量引用什么類型的對(duì)象,變量就是什么類型的。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

In [32]: var1=object
In [33]: var2=var1
In [
34]: id(var1) Out[34]: 139697863383968In [35]: id(var2) Out[35]: 139697863383968

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

PS:id()是python的內(nèi)置函數(shù),用于返回對(duì)象的身份,即對(duì)象的內(nèi)存地址。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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ì)象。

 

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

  在Python中,每個(gè)對(duì)象都有指向該對(duì)象的引用總數(shù)---引用計(jì)數(shù)

  查看對(duì)象的引用計(jì)數(shù):sys.getrefcount()

1、普通引用

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

注意:

  當(dāng)使用某個(gè)引用作為參數(shù),傳遞給getrefcount()時(shí),參數(shù)實(shí)際上創(chuàng)建了一個(gè)臨時(shí)的引用。因此,getrefcount()所得到的結(jié)果,會(huì)比期望的多1。

2、容器對(duì)象

  Python的一個(gè)容器對(duì)象(比如:表、詞典等),可以包含多個(gè)對(duì)象。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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è)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

由上可見,實(shí)際上,容器對(duì)象中包含的并不是元素對(duì)象本身,是指向各個(gè)元素對(duì)象的引用。

3、引用計(jì)數(shù)增加

  1、對(duì)象被創(chuàng)建

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

In [39]: getrefcount(123)
Out[39]: 6In [40]: n=123In [41]: getrefcount(123)
Out[41]: 7

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

  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ì)象本身被銷毀

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

In [50]: a.remove(123)
In [51]: a
Out[51]: [1, 12]

In [52]: getrefcount(123)
Out[52]: 6

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

  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ì)象。

 

四、內(nèi)存池機(jī)制

  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)行操作

 平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

@author:http://www.cnblogs.com/geaozhang/

標(biāo)簽: Python

http://www.cnblogs.com/geaozhang/p/7111961.html