Python的記憶體管理機制(一 小記憶體塊的申請)

2021-07-24 20:39:25 字數 3938 閱讀 9934

哈哈哈,看的有點兒興奮了,筆記下:

在python的內部同時維護著巨集和記憶體管理函式兩種記憶體管理機制,巨集定義可以節省一次函式呼叫的開銷,

提高執行效率,但同時,使用巨集是危險的,因為隨著python的演進,記憶體管理機制的實現可能會發生改變,因為巨集的名字雖然是不會改變的,但其實現**可能會發生改變,導致舊的巨集編寫的c擴充套件與新版本的python產生二進位制不相容,這樣十分危險,所以,在python的內部維護了函式和巨集兩套介面,自己編寫擴充套件時,最好使用函式

1 小塊的記憶體池

在python執行的過程中,很多時候都是小塊記憶體的申請,申請後又很快釋放,也就意味著大量的malloc和free

操作,導致作業系統頻繁的在使用者態和核心態之間進行切換,影響py的執行效率,為了解決這個問題py引入了快取池的機制(pymalloc),整個小塊記憶體的記憶體池可以視為乙個層次結構,在這個層次可以分為4層,從上到下依次為:block , pool , arean和記憶體池

1.1 先看下block吧:

在最底層block是乙個確定大小的記憶體,在py中有很多種block,每個block有不同的記憶體大小,這個記憶體的大小稱為size class,可以不記得,記得這是最小單元就好,所有block事8位元組對齊的,同時,py為block的大小設定了乙個上限,當申請的記憶體大小小於這個上限時,py可以使用不同種類的block來滿足需求,大於這個上限的時候,py轉交給第一次的記憶體管理機制來處理,即pymem函式族,在py2.5版本,這個值為256k

不同種類的block的大小分別為 8,16,32 ..... ,256,每個size class對應乙個size class index ,這個index從0開始,假設申請記憶體為28,會得到乙個32的block,能滿足的最小值

1.2 那麼超過256的記憶體申請怎麼辦呢 ? 來一起看下pool吧:

/* pool for small blocks. */

struct pool_header ref;/* number of allocated blocks    */

block *freeblock;/* pool's free list head         */

struct pool_header *nextpool;/* next pool of this size class  */

struct pool_header *prevpool;/* previous pool       ""        */

uint arenaindex;/* index into arenas of base adr */

uint szidx; /* block size class index */

uint nextoffset;/* bytes to virgin block */

uint maxnextoffset;/* largest valid nextoffset */};

一組block的集合稱為pool,也就是乙個pool管理著多個有固定大小的block,乙個pool的大小通常是乙個記憶體頁,py將其定義為4k,乙個pool裡邊的block的大小是固定的,比如,可能管理了100個32k的block,也可能管理了100個64k的block,但是不可能乙個pool裡既有32k的block也有64k的block,每乙個pool都和乙個size聯絡到一起,更確切的說是和乙個size class index聯絡到一起,這就是pool header裡邊的sindex的意義所在

一起看下pool是怎麼把block組合到一起的:/*

* initialize the pool header, set up the free list to

* contain just the second block, and return the first

* block.

*/pool->szidx = size;

size = index2size(size);

bp = (block *)pool + pool_overhead;

pool->nextoffset = pool_overhead + (size << 1);

pool->maxnextoffset = pool_size - size;

pool->freeblock = bp + size;

*(block **)(pool->freeblock) = null;

unlock();

return (void *)bp;

pool中的第一塊記憶體是分配給pool_header的,所以pool的結構體中ref.count是1,第一塊位址被分配後返回bp的是乙個實際位址(也就是申請pool之後的返回),在這個位址之後會有進4k的記憶體都是實際可用的,但是可以肯定的是申請記憶體的函式只會使用[bp,bp+size]這個區間的記憶體,這是size class index保證的,

我該怎麼畫圖呢 ? ......先跳過了/*

* there is a used pool for this size class.

* pick up the head block of its free list.

*/++pool->ref.count;

bp = pool->freeblock;

assert(bp != null);

if ((pool->freeblock = *(block **)bp) != null)

/* * reached the end of the free list, try to extend it.

*/if (pool->nextoffset <= pool->maxnextoffset)

假設連續申請5塊大小為28位元組的記憶體,由於28位元組的對應size class index為3,所以會申請5塊32位元組的記憶體,原來freeblock執行的是下乙個可用bock的起始位址,再次申請是,只需要返回freeblock指向的位址就可

以了,很顯然,freeblock需要前進,指向下乙個可用block,此時nextoffset就發光發熱了,在pool header裡,nextoffset和maxoffset是對pool中的block步進迭代的變數,偏移位址恰好是下乙個block的可用位址,在分配

完乙個block之後,freeblock和nextoffset就移動乙個block的距離,maxoffset則定義了最大的可用大小,劃定了block的邊界,nextoffset > maxoffset的時候,pool中的block就遍歷完了,恩,就是這樣的,申請->前進->

申請->前進......

但是假如乙個block用完釋放了,假如申請了連續5塊32位元組的記憶體,2用完釋放了,下乙個申請是用2還是6 ?

一旦py執行會有大量的這種記憶體碎片產生,py必須用一種機制把離散的記憶體管理起來,這就是自由的block鍊錶,鍊錶的關鍵就是header中的freeblock,回到上邊,pool申請初始化完成之後,會指向乙個有效位址,為下乙個可分配的記憶體位址,還設定了乙個*freeblock,等到有記憶體釋放的時候,freeblock就會變成乙個小精靈,

在這4k的記憶體上靈活舞動,哈哈~~

pool = pool_addr(p);

if (py_address_in_range(p, pool))

來看下,block釋放的時候,freeblock指向乙個有效的pool位址,但是*freeblock是null,假設釋放的block是blocka,在pool->freeblock = (block *)p;的時候,freeblock的值被更新了,指向了blocka的首位址,在釋放下乙個block的時候,同樣通過

*(block **)p = lastfree = pool->freeblock;

pool->freeblock = (block *)p;

把釋放的block連線到一起,形成自由鍊錶,在需要記憶體的時候可以很方便的遍歷這個鍊錶,優先使用被釋放的記憶體,當pool->freeblock為null的時候就不存在離散的自由鍊錶了,分配nextblock即可,nextoffset > maxoffset時,乙個pool就用完了,怎麼辦 ?哈哈,莫著急,一定是存在乙個pool的集合的,這就是下邊要說的arean

Python記憶體管理機制

一 python記憶體 因為要呼叫while迴圈,迴圈內有temp變數,不清楚python是否會在每一輪迴圈結束後自動釋放temp記憶體空間,做了乙個記憶體測試,發現無論temp none,還是del temp,只能銷毀變數,無法完全釋放記憶體空間。注 紅色部分標出相同記憶體id。python vi...

python記憶體管理機制

a 1整數1為乙個物件。而a是乙個引用。利用賦值語句,引用a指向物件1。在python中,整數和短小的字元,python都會快取這些物件,以便重複使用。當我們建立多個等於1的引用時,實際上是讓所有這些引用指向同乙個物件。a 1 b 1 print id a print id b 在python中,每...

python記憶體管理機制

1.引用計數 當乙個python物件被引用時 其引用計數增加 1 當其不再被變數引用時 引用計數減 1 當物件引用計數等於 0 時,物件被刪除 引用計數是一種非常高效的記憶體管理機制 2.垃圾 垃圾 機制 引用計數 標記清除 分帶 引用計數 引用計數也是一種垃圾收集機制,而且也是一種最直觀,最簡單的...