記得在許多年前,我們的開發環境遠不及現今。除了開發工具、設計觀念和現在遠不能相比之外,就連系統所運行的作業系統,都充滿許多不確定性。尤其像早期的Windows作業系統,始終有記憶體漏失(Memory Leak)的問題。
當你持續要求作業系統配置記憶體,即使依照記憶體配置的規範,作業系統配置給你多少空間,你就歸還多少空間,但是由於某些作業系統本身的瑕疵,系統並沒有辦法完整回收你所歸還的記憶體空間,使得有部分的記憶體空間,是既未被回收,也無法再被配置的,久而久之,最後系統便會因為缺乏可用的記憶體空間,而無法運作下去。
這會影響系統長期運作的能力,所以是很嚴重的問題。當問題出在作業系統時,其實對應用系統的開發者而言,構成很大的困擾。
開挖一個記憶體池塘
還記得多年前我所就讀的大學,圖書館的某系統,必須定期每周重新開機一次,多半就是因為此類的問題所導致。為了解決此類的問題,當時的我們採用一種叫做「Memory Pool」的方法,避免重複向作業系統要求配置與歸還記憶體的動作。
Pool就是池塘的意思,我們可以想像一個大大的池塘,裡頭可以擺放開發者想要擺放的物件,可以從中取出物件,也可以將物件還回池中。Memory Pool的應用中,我們想要擺進池塘裡的,就是記憶體空間區塊。
在我們當時開發的系統中,有一個記憶體配置的模組,當系統中的其他用戶端需要配置記憶體空間時,便會使用該記憶體配置模組所提供的函式,傳入所需的記憶體空間大小,接著此函式便會回傳足夠大小的記憶體空間給用戶端。
當用戶端不再需要它所配置得來的記憶體空間,便呼叫記憶體配置模組的函式歸還空間。對用戶端而言,行為沒有什麼改變,只不過將配置、釋放的對象,由作業系統改為我們設計的模組罷了。
我們設計的記憶體配置模組,基本上是一組Memory Pool的管理程式。Memory Pool是怎麼運作的呢? Pool中擺放的都是記憶體區塊,當用戶端想要配置記憶體時,便檢查Memory Pool中是否有足夠大小的記憶體區域,倘若有,便直接取出供用戶端使用;倘若無,便向作業系統要求一塊夠大的空間,回傳給用戶端。當用戶端歸還記憶體時,便放回Memory Pool,不歸還給作業系統。
在上述的機制中,我們的Memory Pool只有在池塘中沒有足夠大小的記憶體區塊時,才會向作業系統配置新的區塊,否則,會盡量回收使用已經在池塘中的記憶體區塊。
在這樣的設計下,系統並不會反覆要求作業系統配置又釋放記憶體,只會向作業系統要求配置,但盡可能地重複運用配置得來的記憶體,規避記憶體漏失的問題。
資料庫連線也適合套用Object Pool
Memory Pool正是典型的Object Pool,只不過放入池塘裡的,恰好是記憶體區塊罷了。在實際的應用中,有許多不同型態的物件,都很適合利用Object Pool。就以最常聽到的Database Connection Pool為例,便是透過Object Pool的機制管理與使用資料庫連線物件。
為什麼資料庫的連線物件適合套用Object Pool呢?適合使用Object Pool的物件多半具有下述的特性:
1. 具稀少性:資料庫伺服器時常會有連線數的限制,過多的資料庫連線會造成資料庫伺服器的效能低落,因此一般不會建立過多的連線至資料庫。
2. 產生或清理的成本高昂:要產生一個連至資料庫的連線,通常會建立一個TCP連線至資料庫,並且執行資料庫協定的handshaking動作,而這會耗上許多時間。倘若在程式中,每執行一行SQL述句,就重新產生一個資料庫連線物件,那麼等於每次都要耗費許多時間建立連線。
3. 可重複使用:執行SQL述句時需要資料庫連線,執行完後,仍然可以重複使用相同的連線,以便繼續執行其他的述句,這意謂著資料庫連線,是可重複使用的資源。
如果在系統中,發現具上述特性的物件,那麼便可基於效能及資源善用的考量,採用Object Pool產生和管理物件。
Object Pool的典型機制
利用Object Pool產生或提供物件,不會在需要物件時,一再地重新產生物件,只需從池中取出既有且可用的物件,因為物件產生的動作,早在首次被使用時執行過,因而能夠提升配置物件的效能。
典型的Object Pool具備下述的機制:
1. 有一個真正提供物件產生的來源,例如記憶體區塊是由作業系統提供,而JDBC連線物件則是由JDBC驅動程式提供。
2. Object Pool通常會有個給定的容量,限制池中的物件個數。
3. Object Pool可以有個初始的數量,使得Object Pool在一開始,就具備一定數量的物件以供用戶端使用。
4. 當用戶端向Object Pool要求物件,但Object Pool已無可用物件,且配置出去的物件已達上限時,Object Pool可能會有兩種實作策略:讓用戶端等待,直到有其他程式歸還物件;或者不等待,直接回傳配置失敗。
5. Object Pool可能會有動態維持池中物件個數的機制,例如將池中閒置過久的物件,先行歸還給物件來源,避免配置過多不必要的資源。
除了剛才所提到的Memory Pool及Database Connection Pool外,開發系統時,還時常運用到像是Process Pool、Thread Pool、HTTP Connection Pool之類的Object Pool。
你可以選擇針對特定類型的Object Pool,實作上述的機制,或者是建構通用性質的Object Pool,再基於這個通用的Object Pool,實作特定類型的Object Pool。例如,像Jakarta Apache Commons計畫中的Pool程式庫,便是通用性質的Object Pool。
開發可以透過它產生Object Pool,並將想要Pooling的物件置於其中,並藉此獲得Object Pool機制的支援。而在Jakarta Apache的Commons計畫中,還有另一個被稱為DBCP(DataBase Connection Pooling)的程式庫,乃是基於Object Pool而提供專門針對Java資料庫連線的Connection Pooling機制。它利用Pool程式庫所提供的一般性Object Pool機制,再針對資料庫連線物件的特性予以加強。
以Singleton為例,某種程度來說,像是容量只有1的Object Pool,所以Object Pool有時也被稱為是Singleton的特例。
盡管二者在性質上有通用跟特例的關係,但Object Pool的運用與否,基本上是取決於想產生、使用的物件是否具稀少性、產生或清理的成本是否高昂、並且具備可重複使用的特性。倘若這些條件滿足,意謂著是使用Object Pool的時機了。
《作者簡介》王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。
相關閱讀:
探索產生物件的技巧(1)當心隨手New一下引發的衝擊效應
探索產生物件的技巧(2)生成模式的初階應用
探索產生物件的技巧(3)鬆綁程式內工廠與產品的關聯性
探索產生物件的技巧(4)抽象可抵擋變動引發的星星之火
探索產生物件的技巧(5)維持類別僅存在一份實例的設計方法
探索產生物件的技巧(7)避免時效性不高的重複性動作
探索產生物件的技巧(終)間接產生物件的訴求:降低相依性
熱門新聞
2025-01-26
2025-01-25
2025-01-26
2025-01-27
2025-01-27
2025-01-26
2025-01-27