在比較淺層次上我們通過說明如下問題來進(jìn)一步深入了解python內(nèi)存管理機(jī)制:
Python中到底是“傳引用”還是“傳值”呢?渴逻?悠轩?
這個(gè)問題的回答是:看情況喉磁。有的是傳值谓苟,有的是傳引用。
判斷的依據(jù)是看對(duì)象的可變性协怒,而這一點(diǎn)又取決于對(duì)象的類型涝焙。故在python中的說法是對(duì)象可變的還是不可變的。
基本數(shù)據(jù)類型(整型及其他數(shù)據(jù)類型孕暇,字符串)及元組是不可變的仑撞,參數(shù)傳遞的是形參,也就是傳過來的是原值的一個(gè)拷貝妖滔,在函數(shù)中改變形參的值實(shí)參不會(huì)發(fā)生變化:
def func(a)
列表隧哮、字典、類及類實(shí)例是可變數(shù)據(jù)類型座舍,作為參數(shù)傳遞則是原值的一個(gè)引用沮翔,函數(shù)對(duì)形參列表進(jìn)行了改變承二,那么實(shí)參也相應(yīng)的發(fā)生變化:
def func(a=[])
可以使用元組進(jìn)行參數(shù)傳遞,因?yàn)樵M是不允許改變的.
一個(gè)常用的例子是list.sort(),它是直接在列表上排序而不是返回它,可以通過自己復(fù)制一個(gè)拷貝: newlist=list(mylist)塑荒,或者newlist=mylist[:]
python中復(fù)制對(duì)象(a=b)也是同樣的道理凌箕,不可變對(duì)象被真正復(fù)制芜壁,而可變對(duì)象只是復(fù)制了一個(gè)對(duì)它的引用塞淹。
例1:
list0=[1,2,’a’,[‘b’,’c’]]
list1= list0
list1[0]=11
list0[3][0]= ’d’
print list1 #[ 11,2,’a’,[‘d’,’c’] ]
print list0 #[ 11,2,’a’,[‘d’,’c’] ]
list0中1,2,’a’均為不可變對(duì)象费彼,[‘b’,’c’]為可變對(duì)象
例2:(淺拷貝)
list0=[1,2,’a’,[‘b’,’c’]]
list1=list(list0)
list1[0]=11
list0[3][0]= ’d’
print list1 #[ 11,2,’a’,[‘d’,’c’] ]
print list0 #[ 1 ,2,’a’,[‘d’,’c’] ]
list0中1,2,’a’均為不可變對(duì)象小染,[‘b’,’c’]為可變對(duì)象
經(jīng)過上面的講解调榄,可能我們已經(jīng)初步認(rèn)識(shí)到哪些是引用缤灵,哪些是傳值的了胚嘲,至此口注,我們還沒有真正了解它的內(nèi)部原理:
在來看如下例子:
>>a=1
>>b=a
id(a)==id(b)材部,此時(shí)b和a指向同一地址物臂,此時(shí)b也是對(duì)a的一個(gè)引用
當(dāng)b的值改變時(shí)會(huì)指向其他地址沉桌,即重新分配內(nèi)存
>>b+=1
id(a)!=id(b)
>>a=[1,2,3]
>>b=a
//此時(shí)b是a的一個(gè)引用。id(a)==id(b)
>>b=[4,5,6]
//此時(shí)b重新分配內(nèi)存算吩。
另外一點(diǎn)說明:當(dāng)我們定義的變量與函數(shù)同名時(shí)留凭,再使用系統(tǒng)函數(shù),python會(huì)提示不可用偎巢,這時(shí)我們要 del varible將變量刪除:
如:
>>>list=[1,2,3]
>>>newlist=list(list)//這時(shí)外面的list也會(huì)被當(dāng)做列表來使用造成錯(cuò)誤
>>>del list
原理:
python中任何變量都是對(duì)象冰抢,所以參數(shù)只支持引用傳遞方式。即通過名字綁定的機(jī)制艘狭,把實(shí)際參數(shù)的值和形式參數(shù)的名稱綁定在一起,形式參數(shù)和實(shí)際參數(shù)指向內(nèi)存中的同一個(gè)存儲(chǔ)空間翠订。
python中任何變量都是對(duì)象巢音,看上去不可變的對(duì)象是值傳遞其實(shí)也是傳遞的引用,之所以改變形參后尽超,形參和實(shí)參未保持一致是因?yàn)椴豢勺儗?duì)象的改變都是一種重新賦值(即重新分配內(nèi)存的過程)官撼,而可變對(duì)象的改變值就是在原內(nèi)存地址基礎(chǔ)上add或append,不是一個(gè)重新賦值的過程似谁,通過上面的例子我們可以發(fā)現(xiàn)傲绣,可變對(duì)象重新賦值地址是同樣會(huì)發(fā)生變化的。
進(jìn)一步我們來解讀python 的內(nèi)存管理機(jī)制:
Python引入了一個(gè)機(jī)制:引用計(jì)數(shù)巩踏。
python內(nèi)部使用引用計(jì)數(shù)秃诵,來保持追蹤內(nèi)存中的對(duì)象,Python內(nèi)部記錄了對(duì)象有多少個(gè)引用塞琼,即引用計(jì)數(shù)菠净,當(dāng)對(duì)象被創(chuàng)建時(shí)就創(chuàng)建了一個(gè)引用計(jì)數(shù),當(dāng)對(duì)象不再需要時(shí)彪杉,這個(gè)對(duì)象的引用計(jì)數(shù)為0時(shí)毅往,它被垃圾回收。
總結(jié)一下對(duì)象會(huì)在一下情況下引用計(jì)數(shù)加1:
1.對(duì)象被創(chuàng)建:x=4
2.另外的別人被創(chuàng)建:y=x
3.被作為參數(shù)傳遞給函數(shù):foo(x)
4.作為容器對(duì)象的一個(gè)元素:a=[1,x,'33']
引用計(jì)數(shù)減少情況
1.一個(gè)本地引用離開了它的作用域派近。比如上面的foo(x)函數(shù)結(jié)束時(shí)攀唯,x指向的對(duì)象引用減1。
2.對(duì)象的別名被顯式的銷毀:del x 渴丸;或者del y
3.對(duì)象的一個(gè)別名被賦值給其他對(duì)象:x=789
4.對(duì)象從一個(gè)窗口對(duì)象中移除:myList.remove(x)
5.窗口對(duì)象本身被銷毀:del myList侯嘀,或者窗口對(duì)象本身離開了作用域另凌。
垃圾回收
1、當(dāng)內(nèi)存中有不再使用的部分時(shí)残拐,垃圾收集器就會(huì)把他們清理掉途茫。它會(huì)去檢查那些引用計(jì)數(shù)為0的對(duì)象,然后清除其在內(nèi)存的空間溪食。當(dāng)然除了引用計(jì)數(shù)為0的會(huì)被清除囊卜,還有一種情況也會(huì)被垃圾收集器清掉:當(dāng)兩個(gè)對(duì)象相互引用時(shí),他們本身其他的引用已經(jīng)為0了错沃。
2栅组、垃圾回收機(jī)制還有一個(gè)循環(huán)垃圾回收器, 確保釋放循環(huán)引用對(duì)象(a引用b, b引用a, 導(dǎo)致其引用計(jì)數(shù)永遠(yuǎn)不為0)。
在Python中枢析,許多時(shí)候申請(qǐng)的內(nèi)存都是小塊的內(nèi)存玉掸,這些小塊內(nèi)存在申請(qǐng)后,很快又會(huì)被釋放醒叁,由于這些內(nèi)存的申請(qǐng)并不是為了創(chuàng)建對(duì)象司浪,所以并沒有對(duì)象一級(jí)的內(nèi)存池機(jī)制。這就意味著Python在運(yùn)行期間會(huì)大量地執(zhí)行malloc和free的操作把沼,頻繁地在用戶態(tài)和核心態(tài)之間進(jìn)行切換啊易,這將嚴(yán)重影響Python的執(zhí)行效率。為了加速Python的執(zhí)行效率饮睬,Python引入了一個(gè)內(nèi)存池機(jī)制租谈,用于管理對(duì)小塊內(nèi)存的申請(qǐng)和釋放。
內(nèi)存池機(jī)制
Python提供了對(duì)內(nèi)存的垃圾收集機(jī)制捆愁,但是它將不用的內(nèi)存放到內(nèi)存池而不是返回給操作系統(tǒng)割去。
Python中所有小于256個(gè)字節(jié)的對(duì)象都使用pymalloc實(shí)現(xiàn)的分配器,而大的對(duì)象則使用系統(tǒng)的 malloc昼丑。另外Python對(duì)象呻逆,如整數(shù),浮點(diǎn)數(shù)和List菩帝,都有其獨(dú)立的私有內(nèi)存池页慷,對(duì)象間不共享他們的內(nèi)存池。也就是說如果你分配又釋放了大量的整數(shù)胁附,用于緩存這些整數(shù)的內(nèi)存就不能再分配給浮點(diǎn)數(shù)酒繁。
在Python中,許多時(shí)候申請(qǐng)的內(nèi)存都是小塊的內(nèi)存控妻,這些小塊內(nèi)存在申請(qǐng)后州袒,很快又會(huì)被釋放,由于這些內(nèi)存的申請(qǐng)并不是為了創(chuàng)建對(duì)象弓候,所以并沒有對(duì)象一級(jí)的內(nèi)存池機(jī)制郎哭。這就意味著Python在運(yùn)行期間會(huì)大量地執(zhí)行malloc和free的操作他匪,頻繁地在用戶態(tài)和核心態(tài)之間進(jìn)行切換,這將嚴(yán)重影響 Python的執(zhí)行效率夸研。為了加速Python的執(zhí)行效率邦蜜,Python引入了一個(gè)內(nèi)存池機(jī)制,用于管理對(duì)小塊內(nèi)存的申請(qǐng)和釋放亥至。這也就是之前提到的 Pymalloc機(jī)制悼沈。
關(guān)于內(nèi)存管理機(jī)制引用自:http://blog.chinaunix.net/uid-26602509-id-3506965.html