內(nèi)存分配
Python中的內(nèi)存管理機(jī)制的層次結(jié)構(gòu)提供了4層
最底層則是C運(yùn)行的malloc和free接口
第一層則是在第0層的基礎(chǔ)之上對(duì)其提供的接口進(jìn)行了統(tǒng)一的封裝笤闯,這是因?yàn)殡m然不同的操作系統(tǒng)都提供標(biāo)準(zhǔn)定義的內(nèi)存管理接口望蜡,但是對(duì)于某些特殊的情況不同的操作系統(tǒng)都不同的行為,比如說(shuō)調(diào)用malloc(0)棕孙,有的操作系統(tǒng)會(huì)返回NULL舔亭,表示內(nèi)存申請(qǐng)失斝┡颉;然而有的操作系統(tǒng)會(huì)返回一個(gè)貌似正常的指針钦铺,但是這個(gè)指針?biāo)傅膬?nèi)存并不是有效的订雾。為了廣泛的移植性,Python必須保證相同的語(yǔ)義一定代表相同的運(yùn)行行為职抡。
在第二層內(nèi)存管理機(jī)制上葬燎,Python構(gòu)建了更高抽象的內(nèi)存管理策略,比如說(shuō)一些常用對(duì)象缚甩,包括整數(shù)對(duì)象谱净、字符串對(duì)象等等。
第三層主要是對(duì)象緩沖池機(jī)制擅威,它基于在第二層的內(nèi)存池壕探。
內(nèi)存池
Python為了避免頻繁的申請(qǐng)和刪除內(nèi)存所造成系統(tǒng)切換于用戶態(tài)和核心態(tài)的開(kāi)銷,從而引入了內(nèi)存池機(jī)制郊丛,專門(mén)用來(lái)管理小內(nèi)存的申請(qǐng)和釋放李请。整個(gè)小塊內(nèi)存的內(nèi)存池可以視為一個(gè)層次結(jié)構(gòu),其一共分為4層厉熟,從下之上分別是block导盅、pool、arena和內(nèi)存池揍瑟。需要說(shuō)明的是:block白翻、pool和area都是代碼中可以找到的實(shí)體,而最頂層的內(nèi)存池只是一個(gè)概念上的東西绢片,表示Python對(duì)于整個(gè)小塊內(nèi)存分配和釋放行為的內(nèi)存管理機(jī)制滤馍。
內(nèi)存大小以256字節(jié)為界限,大于則通過(guò)malloc進(jìn)行分配底循,小于則通過(guò)內(nèi)存池分配巢株。
block:最小的內(nèi)存單元,大小為8的整數(shù)倍熙涤。有很多種類的block阁苞,不同種類的block都有不同的內(nèi)存大小,申請(qǐng)內(nèi)存的時(shí)候只需要找到適合自身大小的block即可祠挫,當(dāng)然申請(qǐng)的內(nèi)存也是存在一個(gè)上限猬错,如果超過(guò)這個(gè)上限,則退化到使用最底層的malloc進(jìn)行申請(qǐng)茸歧。
pool:一個(gè)pool管理著一堆有固定大小的內(nèi)存塊倦炒,其大小通常為一個(gè)系統(tǒng)內(nèi)存頁(yè)的大小。
arena:多個(gè)pool組合成一個(gè)arena软瞎。
GC
引用計(jì)數(shù)
python里每一個(gè)東西都是對(duì)象逢唤,它們的核心就是一個(gè)結(jié)構(gòu)體:
typedef struct_object {?
?????int ob_refcnt;?
?????struct_typeobject *ob_type;
} PyObject;
PyObject,是每個(gè)對(duì)象必有的內(nèi)容拉讯,其中ob_refcnt就是做為引用計(jì)數(shù),引用計(jì)數(shù)為0時(shí),該對(duì)象生命就結(jié)束了鳖藕。
導(dǎo)致引用計(jì)數(shù)+1的情況
對(duì)象被創(chuàng)建魔慷,例如a=23
對(duì)象被引用,例如b=a
對(duì)象被作為參數(shù)著恩,傳入到一個(gè)函數(shù)中院尔,例如func(a)
對(duì)象作為一個(gè)元素,存儲(chǔ)在容器中喉誊,例如list1=[a,a]
導(dǎo)致引用計(jì)數(shù)-1的情況
對(duì)象的別名被顯式銷毀邀摆,例如del a
對(duì)象的別名被賦予新的對(duì)象,例如a=24
一個(gè)對(duì)象離開(kāi)它的作用域伍茄,例如f函數(shù)執(zhí)行完畢時(shí)栋盹,func函數(shù)中的局部變量(全局變量不會(huì))
對(duì)象所在的容器被銷毀,或從容器中刪除對(duì)象
優(yōu)點(diǎn):
實(shí)時(shí)性:一旦沒(méi)有引用敷矫,內(nèi)存就直接釋放了例获。不用像其他機(jī)制等到特定時(shí)機(jī)。實(shí)時(shí)性還帶來(lái)一個(gè)好處:處理回收內(nèi)存的時(shí)間分?jǐn)偟搅似綍r(shí)曹仗。
缺點(diǎn):
維護(hù)引用計(jì)數(shù)消耗資源
循環(huán)引用
標(biāo)記清除
標(biāo)記-清除只關(guān)注那些可能會(huì)產(chǎn)生循環(huán)引用的對(duì)象榨汤,顯然,像是PyIntObject怎茫、PyStringObject這些不可變對(duì)象是不可能產(chǎn)生循環(huán)引用的件余,因?yàn)樗鼈儍?nèi)部不可能持有其它對(duì)象的引用。Python中的循環(huán)引用總是發(fā)生在container對(duì)象之間遭居,也就是能夠在內(nèi)部持有其它對(duì)象的對(duì)象,比如list旬渠、dict俱萍、class等等
原理:1. 尋找跟對(duì)象(root object)的集合作為垃圾檢測(cè)動(dòng)作的起點(diǎn),跟對(duì)象也就是一些全局引用和函數(shù)棧中的引用告丢,這些引用所指向的對(duì)象是不可被刪除的枪蘑;2. 從root object集合出發(fā),沿著root object集合中的每一個(gè)引用岖免,如果能夠到達(dá)某個(gè)對(duì)象岳颇,則說(shuō)明這個(gè)對(duì)象是可達(dá)的,那么就不會(huì)被刪除颅湘,這個(gè)過(guò)程就是垃圾檢測(cè)階段话侧;3. 當(dāng)檢測(cè)階段結(jié)束以后,所有的對(duì)象就分成可達(dá)和不可達(dá)兩部分闯参,所有的可達(dá)對(duì)象都進(jìn)行保留瞻鹏,其它的不可達(dá)對(duì)象所占用的內(nèi)存將會(huì)被回收悲立,這就是垃圾回收階段。(底層采用的是鏈表將這些集合的對(duì)象連接在一起)
分代回收
將系統(tǒng)中的所有內(nèi)存塊根據(jù)其存活時(shí)間劃分為不同的集合新博,每一個(gè)集合就成為一個(gè)“代”薪夕,Python默認(rèn)定義了三代對(duì)象集合,垃圾收集的頻率隨著“代”的存活時(shí)間的增大而減小赫悄。也就是說(shuō)原献,活得越長(zhǎng)的對(duì)象,就越不可能是垃圾埂淮,就應(yīng)該減少對(duì)它的垃圾收集頻率姑隅。那么如何來(lái)衡量這個(gè)存活時(shí)間:通常是利用幾次垃圾收集動(dòng)作來(lái)衡量,如果一個(gè)對(duì)象經(jīng)過(guò)的垃圾收集次數(shù)越多同诫,可以得出:該對(duì)象存活時(shí)間就越長(zhǎng)粤策。
import gc模塊,并且is_enable()=True才會(huì)啟動(dòng)自動(dòng)垃圾回收误窖。