日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python内存池管理与缓冲池设计

發布時間:2025/7/14 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python内存池管理与缓冲池设计 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

出處:http://blog.csdn.net/zhzhl202/article/details/7547445#t4

Python是一門開發效率很高的語言,而且其既下里巴人,又陽春白雪。也就是說這門語言只要稍加學習就可以上手開發,而深入探究也會發現Python有很多高深的東西。最近讀了《Python源碼剖析》,收獲良多,今天就把Python的內存管理整理一番。

本文的組織如下:
第一部分:整理Python的內存管理機制,主要包括內存池以及對象緩存池
第二部分:從百度的筆試題來探討如何實現一個緩存池。


第一部分:整理Python的內存管理機制

Python的內存管理內存總共分為4層,如下圖所示



其中Layer2為內存池,Layer3為對象緩沖池。Python的對象緩沖池Layer3是建立在Layer2基礎上的。

本文重點總結內存池和對象緩沖池。


內存池


ython又分為大內存和小內存。大小以256字節為界限,對于大內存使用Malloc進行分配,而對于小內存則使用內存池進行分配。

Python的內存池又分為4個層次:Block、Pool、Arean、usedpool,如下圖所示



其中block是最小的內存單元,大小為8的整數倍。如果想申請27B的內存,會分配一個32B的block,其中申請size和size_index之間的關系有對應,見下圖




其中block是最小的內存單元,大小為8的整數倍。如果想申請27B的內存,會分配一個32B的block,其中申請size和size_index之間的關系有對應,見下圖




對象緩沖池


整數對象緩沖池

對于[-5,257]這樣的小整數,系統已經初始化好,可以直接拿來用。而對于其他的大整數,系統則提前申請了一塊內存空間,等需要的時候在這上面創建大整數對象。


最上面的為PyInBlock結構,每個PyInBlock中是一個PyIntObject數組。

而連續數組在內存不斷進行申請和釋放中就會出現不連續的空閑塊,因此需要把所有的空閑內存塊組織起來,這樣就需要使用free_list進行組織




2. String對象緩沖池

Python為256個Ascii碼進行了對象緩沖池,見下圖



這里面的過程是先對所創建的字符對象進行Intern機制,再將intern的結果緩存到字符串緩沖池characters中。所謂的intern機制是指在系統中建立一個(key,value)映射關系的集合,記錄著被intern機制處理過的PyStringObject對象。對于被Intern之后的字符串,在整個Python運行時,系統中都只有唯一的與該字符串對應的PyStringObject對象。這樣當判斷兩個PyStringObject對象是否相同時,如果它們都被Intern了,那么只需要簡單地檢查它們對應的PyObject*是否相同即可。這個機制既節省了空間,又簡化了對PyStringObject對象的比較



3. List和Dict對象緩沖池



第二部分 設計緩沖池


說完了Python的內存管理,我們再來探討一下如何設計并實現一個對象緩沖池。首先來看一下百度的一個筆試題。

設計一個緩沖池,用于存放系統所需要的資源。滿足如下要求:
(1)當讀取緩沖池資源是,如果沒有該資源,則創建該資源,放入緩沖池中。
(2)緩沖池可以存放各種形式的資源。
(3)要有刷新機制,當一個資源長時間沒有使用時,要把該資源從緩沖池中剔除。
要考慮分配資源的合理性和時效性,緩沖池可以有的參數有最小資源數、最大資源數、timeout等,重點描述一下緩沖池的刷新機制。

先要知道緩沖池是用來干嘛的!所謂緩沖池是為了減少磁盤的IO操作,專門在內存中開辟一塊區域,將磁盤中一些經常訪問的數據放入到該區域,以檢查IO操作。
因此設計一個緩存池需要考慮的幾個問題:
a) 如何對數據庫進行組織,使得可以快速查找到緩存池中的資源
b) 當緩存池滿的時候,使用什么樣的換入換出策略,如何保證數據塊置換的效率
c) 對同一個數據塊的并發訪問


d)維護數據一致性,采用什么樣的寫入策略

緩沖池的概貌



這里的hash bucket就是內存二維數組的第一維。它是通過對buffer header里記錄的數據塊地址和數據塊類型運用hash算法以后,得到的組號。
這里的hash chain就是屬于同一個hash bucket的所有buffer header所串起來的鏈表。實際上,hash bucket只是一個邏輯上的概念。每個hash bucket都是通過不同的hash chain而體現出來的。每個hash chain都會由一個cache buffers chains latch來管理其并發操作。
而對于buffer header來說,每一個數據塊在被讀入buffer cache時,都會先在buffer cache中構造一個buffer header,buffer header與數據塊一一對應。


刷新機制


本文重點討論一下刷新機制。所謂刷新即當一個資源長時間沒有使用時,要把該資源從緩沖池中剔除,怎么樣才能判定一個資源長時間沒有使用呢?參考操作系統中Cache的設計機制,cache中常用換頁機制,采用的有FIFO、LRU、Clock算法。這里我們就是用LRU算法。

LRU

我們舉一個例子。假設緩沖池只能容納4個數據塊,同時只有一個hash chain和一個LRU。當系統剛剛啟動,緩沖池是空的。這時前臺進程獲取數據塊,系統找一個空的內存數據塊,并將其對應的buffer header掛到hash chain上。同時,系統還會把該buffer header掛到LRU的最尾端。隨后前臺進程又發出獲取數據塊請求,這時所找到的buffer header在LRU上會掛到前一個buffer header的后面,也就是說請求所找到的buffer header現在變成了LRU的最尾端了。假設發出4數據請求以后找到了4個buffer header,從而用完了所有的buffer cache空間。這個時候的LRU可以用下圖來表示。


這個時候,進程發來了第5個數據塊請求語句。這時的緩存池里已經沒有空的內存數據塊了。但是既然需要容納下第五個數據塊,就必然需要找一個可以被替換的內存數據塊。這個內存數據塊會到LRU上去找。按照系統設定的最近最少使用的原則,位于LRU最尾端的BH1將成為犧牲者,系統會把該BH1對應的內存數據塊的內容清空,并將當前所獲得的數據塊的內容拷貝進去。這個時候,BH1就成了LRU的首端,而BH2則成為了LRU的尾端。如下圖所示。在這種方式下,經常被訪問的數據塊可以一直靠近LRU的首端,也就保證了這些數據塊可以盡可能的不被替換掉,從而保證了訪問的效率。

加入訪問次數的LRU

OK,假如我們想把每個數據塊的訪問次數加入到數據塊置換策略中,該如何實現呢?
我們來增加一個輔助鏈表。緩沖池有LRU和LRUW兩個鏈表,分別叫做輔助鏈表和主鏈表。同時還對buffer header增加了一個屬性:touch數量,也就是每個buffer header曾經被訪問過的次數,來對LRU鏈表進行管理。系統每訪問一次buffer header,就會將該buffer header上的touch數量增加1,因此,touch數量“近似”的體現了某個內存數據塊總共被訪問的次數。注意,這只是近似,并不精確。因為touch的增加并沒有使用鎖來管理并發性。這只是一個大概值,表示趨勢的,不用百分百的精確。

還是用上面的這個例子來說明。還是假設buffer cache只能容納4個數據塊,同時只有一個hash chain和一個LRU(確切的說應該是一對LRU主鏈表和輔助鏈表)。讀入第一個數據塊時,該數據塊對應的buffer header會掛到LRU輔助鏈表(注意,這里是輔助鏈表,而不是主鏈表)的最末端,同時touch數量為1。讀取第二個不同的數據塊時,該數據塊對應的buffer header會掛到前一個buffer header的后面,從而位于LRU輔助鏈表的最末端,同樣touch為1。假設4個數據塊全都用完以后的LRU鏈表可以用下圖四描述。每個buffer header的touch數量都為1。

上圖中我們可以看到輔助LRU鏈表都掛滿了,而主LRU鏈表還是空的。這個時候,系統要求返回指定的數據塊。系統發現buffer cache里已經沒有空的內存數據塊了,于是從輔助LRU鏈表的尾部開始掃描,也就是從BH1開始掃描,以查找可以被替代的數據塊。這時將選出BH1作為犧牲者,并將其對應的內存數據塊的內容清空,同時將當前第五個數據塊的內容拷貝進去。但是這里要注意,這個時候該BH1在LRU鏈表上的位置并不會發生任何的變化。而不會之前的那樣,BH1變成LRU鏈表的首端。

接下來,系統發出兩次數據塊請求,分別要返回與第5個和第4個一樣的數據塊,也就是要返回當前的BH1和BH4。這個時候,oracle會增加BH1和BH4的touch數量,同時將該BH1和BH4從輔助LRU鏈表上摘下,轉移到主LRU鏈表的中間位置。可以用下圖描述。


這個時候,如果發來了第個數據塊請求,要求返回與第3個相同的數據塊,也就是當前的BH3,則這時該BH3會插入主LRU鏈表上的BH1和BH4中間,注意每次向主LRU列表插入buffer header時都是向中間位置插入。如果發來了第九句SQL要求返回BH2,則我們可以知道,BH2會轉移到主LRU鏈表的中間。這個時候,輔助LRU鏈表就空了,沒有buffer header了。

這時,如果又發來第10個數據塊請求,要求返回一個新的、buffer cache中不存在所需內容的數據塊時。oracle會先掃描輔助LRU鏈表,發現上面沒有任何的buffer header時,則必須掃描主LRU鏈表。從尾部開始掃描,采用前面說到的與掃描輔助LRU鏈表相同的規則挑選犧牲者。挑出的可以被替代的buffer header將從主LRU鏈表上摘下,放入輔助LRU鏈表。

從上面所描述的buffer header在輔助LRU鏈表和主LRU鏈表之間交替的過程中,這種改進LRU鏈表的管理方式的目的,就是想千方百計的能夠將多次被訪問的數據塊保留在內存里,同時又要平衡有限的內存資源。這種方式相比較之前而言,無疑是進步很多的。在之前中,某個數據塊可能只會被訪問一次,但是就這么一次的訪問就將該數據塊放到了LRU的首端,從而可能就擠掉了一個LRU上不是那么經常被訪問,但是也會多次訪問的數據塊。而后面的算法,將訪問一次的數據塊和訪問一次以上的數據塊徹底分開,而且查找可用數據塊時,始終都是從輔助LRU鏈表開始掃描。實際上也就使得越傾向于只訪問一次的數據塊越快的從內存中清理出去。

總結

本文首先根據《Python源碼剖析》整理了Python的內存管理機制,重點討論了內存池以及對象緩存池。在第二部分結合百度的筆試題探討了如何實現一個緩存池,重點討論了緩存池基本架構以及數據塊換出策略。

Further Reading and Reference:

Python內存管理:
1. 《Python源碼剖析》 非常好的一本介紹Python源碼的書籍,里面介紹了Python的對象機制、虛擬機、類、內存管理、垃圾回收等各個方面2. http://blog.donews.com/lemur/?s=Python%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90Python Python源碼剖析的作者的博客,專門介紹CPython源碼的專欄。3. http://docs.python.org/library/index.html ?想了解Python? 官方文檔才是王道!
緩存池相關:
1. http://space.itpub.net/9842/viewspace-399665 緩存池在數據庫用的還是比較多。本文就Oracle中的Buffer cache展開了分析,有理論有示例,非常不錯的文章,本文也引用了這篇文章的相關內容。


轉載于:https://blog.51cto.com/8701404/1375537

總結

以上是生活随笔為你收集整理的Python内存池管理与缓冲池设计的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。