1. 程式人生 > >Python記憶體管理演算法介紹

Python記憶體管理演算法介紹

1      介紹

使用Python語言的一個好處是Python和其它一些高階語言一樣,會進行自動的記憶體管理。它使用引用計數機制檢測為物件分配的記憶體是否可以被釋放。然而,在Python中記憶體永遠不會還給作業系統,Python會持有這些記憶體並在需要時重新使用它們。在很多場景下,這個特性可以減少記憶體申請和釋放所帶來的效能損耗;但對於需要長時間執行的Python程序來講,Python將會佔用大量的記憶體。如果程序使用記憶體的峰值遠大於平均值,這將會造成記憶體的浪費從而影響本程序甚至是系統中其它程序的效能。

下面我們首先了解一下,Python是如何進行記憶體進行管理的。

2      Pymalloc

Python使用Pymalloc管理記憶體。在Python中,會頻繁的建立和刪除很多小物件,如果這些物件的記憶體申請和釋放都使用malloc()和free(),將會帶來嚴重的效能問題。因此,Pymalloc分配一系列256KB的記憶體塊,稱之為arena。每個arena分割為4KB大小的記憶體池Pool,每個Pool再切分為固定大小的Block。在記憶體分配時,分配給程序的就是這些Blocks。

2.1      記憶體分配

上圖中展示了一個usedpool陣列,此陣列按記憶體大小組織,每個大小對應一個Pool連結串列,每個Pool連結串列中有多個空閒的Block。在分配記憶體時,Pymalloc先判斷是否存在要申請的大小的Pool,如果存在的話,直接從Pool中獲取一個Free Block返回給應用程式,這個過程是非常迅速的。如果分配完這個Block後此Pool變為一個空Pool,則將這個Pool從連結串列中移除。

如果在usedpool中找不到大小匹配的Pool,需要在freepool中查詢可用的Pool。在找不到的情況下,首先會嘗試在最後一個arena中看是否存在可用的記憶體,如果有的話則分配一個給freepool使用;如果不存在這樣的arena,將會通過malloc()分配一個新的arena。在freepool中找到一個可用的Pool後,會將此Pool切分為固定大小的Pool並加入到usedpool中,並在其中分配一個Free Block給應用程式。

2.2      記憶體釋放

在應用程式要釋放一個Block時,過程和分配的過程比較相似。首先會根據Block找到此Block所歸屬的Pool,然後將此Block加入到Pool的Free Block列表中。如果Pool當前是空的,還會將這個Pool加入到usedpool的連結串列中。如果在Block加入到Free Block後Pool中所有的Block都是Free的,會將此Pool從usedpool移動到freepool中。

上面的過程可以看到,記憶體釋放的過程基本就是記憶體申請的反過程,但唯一的區別是缺少了將freepool返還給arena,並將arena通過free()返還給作業系統的步驟。

3      基本資料型別的記憶體分配

Python中有一小部分的物件是不使用Pymalloc程序記憶體分配的,主要是integer/float/list/dict。為了提升這些常用物件的記憶體使用效率,這些物件是儲存在單獨的列表中的。

Python通過malloc()為Integer/Float兩種型別分配大約1KB大小的記憶體塊列表,這些列表被當作Integer/Float的陣列使用,而不是使用Pymalloc的分配的8位元組的整數倍大小的Block,以減少記憶體消耗。在建立一個新的Integer/Float物件時,直接從這個記憶體列表中獲取資料,或是重新分配一塊新的Block;在釋放時對應的Block重新加入到列表中。這些Block也是不會被返還給作業系統的。

Python為List/Dict採用不同的策略,會最多保留80個空閒的List/Dict,如果多餘80個,多出的會被釋放。