內(nèi)存管理對(duì)于編寫出高效率的Windows程序是非常重要的,這是因?yàn)閃indows是多任務(wù)系統(tǒng),它的內(nèi)存管理和單任務(wù)的DOS相比有很大的差異。那么python如何進(jìn)行內(nèi)存管理呢?一起來了解下吧:
?
python如何進(jìn)行內(nèi)存管理
?
?
一、對(duì)象的引用計(jì)數(shù)機(jī)制
?
python內(nèi)部使用引用計(jì)數(shù),來保持追蹤內(nèi)存中的對(duì)象,所有對(duì)象都有引用計(jì)數(shù)?
?
引用計(jì)數(shù)增加的情況:?
?
1. 一個(gè)對(duì)象分配一個(gè)新名稱?
?
2. 將其放入一個(gè)容器中?
?
引用計(jì)數(shù)減少的情況:?
?
1. 使用del語句對(duì)對(duì)象別名顯示的銷毀?
?
2. 引用超出作用域或被重新賦值?
?
sys.getrefcount()函數(shù)可以獲得對(duì)象的當(dāng)前引用計(jì)數(shù)?
?
多數(shù)情況下,引用計(jì)數(shù)比你猜測(cè)的要大得多。對(duì)于不可變數(shù)據(jù)(如數(shù)字和字符串),解釋器會(huì)在程序的不同部分共享內(nèi)存,以便節(jié)約內(nèi)存。
?
二、垃圾回收
?
當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)歸零時(shí),它將被垃圾收集機(jī)制處理掉。
?
當(dāng)兩個(gè)對(duì)象a 和b 相互引用時(shí),del語句可以減少a和b的引用次數(shù),并銷毀用于引用底層對(duì)象的名稱。然而由于每個(gè)對(duì)象都包含一個(gè)對(duì)其他對(duì)象的應(yīng)用,因此引用計(jì)數(shù)不會(huì)歸零,對(duì)象也不會(huì)銷毀。(從而導(dǎo)致內(nèi)存泄漏)。為解決這一問題,解釋器會(huì)定期執(zhí)行一個(gè)循環(huán)檢測(cè)器,搜索不可訪問對(duì)象的循環(huán)并刪除它們。
?
三、內(nèi)存池機(jī)制
?
python提供了對(duì)內(nèi)存的垃圾收集機(jī)制,但是它將不用的內(nèi)存放到內(nèi)存池而不是返回給操作系統(tǒng)。?
?
1. Pymalloc機(jī)制。為了加速python的執(zhí)行效率,python引入了一個(gè)內(nèi)存池機(jī)制,用于管理對(duì)小塊內(nèi)存的申請(qǐng)和釋放。?
?
2. python中所有小于256個(gè)字節(jié)的對(duì)象都使用pymalloc實(shí)現(xiàn)的分配器,而大的對(duì)象則使用系統(tǒng)的malloc。?
?
3. 對(duì)于python對(duì)象,如整數(shù),浮點(diǎn)數(shù)和list,都有其獨(dú)立的私有內(nèi)存池,對(duì)象間不共享它們的內(nèi)存池。也就是說如果你分配又釋放了大量的整數(shù),用于緩存這些整數(shù)的內(nèi)存就不能再分配給浮點(diǎn)數(shù)。
?
python怎么內(nèi)存管理
?
內(nèi)存管理
?
包括:
?
變量無須事先聲明
?
變量無須指定類型
?
不用關(guān)心內(nèi)存管理
?
變量名會(huì)被"回收"
?
del 語句能夠直接釋放資源
?
變量定義
?
python中, 變量在*次被賦值時(shí)自動(dòng)聲明, 和其它語言一樣, 變量只有被創(chuàng)建和賦值后才能被使用
?
動(dòng)態(tài)類型
?
變量名無須事先聲明, 也無須類型聲明
?
對(duì)象的類型和內(nèi)存占用都是運(yùn)行時(shí)確定的
?
內(nèi)存分配
?
python解釋器會(huì)自動(dòng)進(jìn)行內(nèi)存管理, 不用開發(fā)人員去關(guān)心
?
引用計(jì)數(shù)
?
要保持追蹤內(nèi)存中的狀態(tài), python使用了引用計(jì)數(shù), 就是python內(nèi)部記錄著所有使用中的對(duì)象各有多少引用.
?
一個(gè)內(nèi)部跟蹤變量, 稱為一個(gè)引用計(jì)數(shù)器, 至于每個(gè)對(duì)象各有多少引用, 簡(jiǎn)稱引用計(jì)數(shù), 當(dāng)對(duì)象被創(chuàng)建時(shí), 就創(chuàng)建了一個(gè)引用計(jì)數(shù), 當(dāng)這個(gè)對(duì)象不再需要時(shí), 也就是說, 這個(gè)對(duì)象的引用計(jì)數(shù)變?yōu)?時(shí), 它被垃圾回收
?
增加引用計(jì)數(shù)
?
當(dāng)對(duì)象被創(chuàng)建賦值給變量時(shí), 該對(duì)象的引用計(jì)數(shù)就被設(shè)置為1
?
當(dāng)同一個(gè)對(duì)象又被賦值給其他變量時(shí), 或作為參數(shù)傳遞給函數(shù), 方法或類實(shí)例時(shí), 或者被賦值為一個(gè)窗口對(duì)象的成員時(shí), 該對(duì)象的一個(gè)新的引用, 或者作為別名, 就被創(chuàng)建.
?
減少引用計(jì)數(shù)
?
當(dāng)對(duì)象的引用被銷毀時(shí), 引用計(jì)數(shù)會(huì)減少, 明顯的例子就是當(dāng)引用離開其作用范圍時(shí), 這種情況最經(jīng)常出現(xiàn)在函數(shù)運(yùn)行結(jié)束時(shí), 所有局部變量都被自動(dòng)銷毀, 對(duì)象的引用計(jì)數(shù)也就減少
?
垃圾收集
?
不再被使用的內(nèi)存會(huì)被一種稱為垃圾收集的機(jī)制釋放
?
注: 解釋器跟蹤對(duì)象的引用計(jì)數(shù), 垃圾回收機(jī)制負(fù)責(zé)釋放內(nèi)存, 垃圾收集器是一塊獨(dú)立代碼, 它用來尋找引用計(jì)數(shù)為0的對(duì)象, 它也負(fù)責(zé)檢查雖然引用計(jì)數(shù)大于0但是也應(yīng)該被銷毀的對(duì)象
?
引用語義: python中在變量里保存值(對(duì)象)的引用, 采用這種方式, 變量所需的存儲(chǔ)空間大小一致, 因?yàn)槠渲恍枰4嬉粋€(gè)引用
?
值語義變量的值直接保存在變量的存儲(chǔ)區(qū)里, 這樣一個(gè)整數(shù)類型的變量就需要保存一個(gè)整數(shù)所需的空間, 一個(gè)浮點(diǎn)數(shù)變量就需要足夠的空間存儲(chǔ)一個(gè)浮點(diǎn)數(shù). C中就是值語義
?
python的內(nèi)存管理機(jī)制
?
先從較淺的層面來說,Python的內(nèi)存管理機(jī)制可以從三個(gè)方面來講
?
(1)垃圾回收
?
(2)引用計(jì)數(shù)
?
(3)內(nèi)存池機(jī)制
?
?
一、垃圾回收:
?
python不像C++,Java等語言一樣,他們可以不用事先聲明變量類型而直接對(duì)變量進(jìn)行賦值。對(duì)Python語言來講,對(duì)象的類型和內(nèi)存都是在運(yùn)行時(shí)確定的。這也是為什么我們稱Python語言為動(dòng)態(tài)類型的原因(這里我們把動(dòng)態(tài)類型可以簡(jiǎn)單的歸結(jié)為對(duì)變量內(nèi)存地址的分配是在運(yùn)行時(shí)自動(dòng)判斷變量類型并對(duì)變量進(jìn)行賦值)。
?
二、引用計(jì)數(shù):
?
Python采用了類似Windows內(nèi)核對(duì)象一樣的方式來對(duì)內(nèi)存進(jìn)行管理。每一個(gè)對(duì)象,都維護(hù)這一個(gè)對(duì)指向該對(duì)對(duì)象的引用的計(jì)數(shù)。如圖所示(圖片來自Python核心編程)
?
、
?
x = 3.14
?
y = x
?
我們首先創(chuàng)建了一個(gè)對(duì)象3.14, 然后將這個(gè)浮點(diǎn)數(shù)對(duì)象的引用賦值給x,因?yàn)閤是*個(gè)引用,因此,這個(gè)浮點(diǎn)數(shù)對(duì)象的引用計(jì)數(shù)為1. 語句y = x創(chuàng)建了一個(gè)指向同一個(gè)對(duì)象的引用別名y,我們發(fā)現(xiàn),并沒有為Y創(chuàng)建一個(gè)新的對(duì)象,而是將Y也指向了x指向的浮點(diǎn)數(shù)對(duì)象,使其引用計(jì)數(shù)為2.
?
python內(nèi)存管理的方法
?
1)動(dòng)態(tài)類型
?
1.1)核心思想
?
python中"一切皆對(duì)象".作為"動(dòng)態(tài)語言",python遵循"對(duì)象與引用分離"的核心思想.
?
1.2)一切皆對(duì)象
?
常見的變量,如整數(shù)、字符串、列表等,在python中一切皆為對(duì)象
?
a = 1
?
整數(shù)1為一個(gè)對(duì)象,是儲(chǔ)存在內(nèi)存中的實(shí)體.
?
對(duì)象名a為一個(gè)引用,我們不能直接接觸到"對(duì)象實(shí)體",只能通過引用指向去訪問,引用可以隨時(shí)指向一個(gè)新的對(duì)象.
?
1.3)賦值操作:可變對(duì)象、不可變對(duì)象
?
不可變數(shù)據(jù)對(duì)象:不能改變對(duì)象本身,只能改變引用的指向;如整形、字符串、元祖等
?
a = 5
?
b = a
?
a = a + 2
?
解析:此時(shí)a = 7,b = 5.驗(yàn)證了不可變對(duì)象只能改變引用的指向,各個(gè)引用相互獨(dú)立,互不影響.
?
可變數(shù)據(jù)對(duì)象:引用其元素(類似于L[0]),改變對(duì)象的本身;如列表,字典等
?
L1 = [1,2,3]
?
L2 = L1
?
L1[0] = 100
?
解析:此時(shí)L1 = L2 = [100,2,3].驗(yàn)證了可變對(duì)象可以通過引用其元素,改變對(duì)象本身
?
1.4)從動(dòng)態(tài)類型看函數(shù)的參數(shù)傳遞
?
def f(x):
?
? ? x = 100
?
? ? print x
?
a = 1
?
f(a)
?
print a
?
解析:參數(shù)x是一個(gè)新的引用,指向a所指的對(duì)象.此處為不可變對(duì)象,引用a和x之間是相互獨(dú)立的.參數(shù)x的操作不會(huì)影響引用a.
?
def f(x):
?
? ? x[0] = 100
?
? ? print x
?
a = [1,2,3]
?
f(a)
?
print a
?
解析:參照上一個(gè)例子,此處為可變對(duì)象傳參,可以通過引用其元素改變對(duì)象本身.所以此處a和x都為[100,2,3]
?
1.5)垃圾回收
?
python采取了一種簡(jiǎn)單的垃圾回收機(jī)制:即引用計(jì)數(shù)(from sys import getrefcount()).垃圾回收時(shí),python不能進(jìn)行其他任務(wù),頻繁的垃圾回收會(huì)大大降低python的工作效率.因此,python會(huì)在特定的情況下,自動(dòng)啟動(dòng)垃圾回收機(jī)制.
?
通過gc模塊查看垃圾回收閥值.
?
import gc
?
print gc.get_threshold()
?
輸出(700,10,10)
?
解析:700是垃圾回收的閥值:內(nèi)存中分配對(duì)象的次數(shù)-取消分配對(duì)象的次數(shù)=700;可以通過set.threshold()設(shè)置.
?
手動(dòng)啟動(dòng)垃圾回收機(jī)制:gc.collect()
?
1.6)分代回收
?
存活越久的對(duì)象,處于信任和效率,越不可能在后面的程序中變?yōu)槔?所以減少垃圾回收掃描他們的頻率.
?
python將所有的對(duì)象分為0,1,2三代.所有新建的對(duì)象都是0帶對(duì)象.當(dāng)經(jīng)過某一次垃圾回收,依然存活,那么它就被歸入下一代對(duì)象.
?
參考1.5,輸出中包含2個(gè)10,分別代表:每10次0代對(duì)象垃圾回收,會(huì)進(jìn)行1次1代對(duì)象垃圾回收;每10次1代對(duì)象垃圾回收,會(huì)進(jìn)行1次2代對(duì)象垃圾回收
?
我們?cè)谄綍r(shí)應(yīng)該避免對(duì)2代對(duì)象頻繁掃描:gc.set_threshold(700,10,20)
?