對(duì)象池技術(shù)原理分析論文

時(shí)間:2022-11-04 03:55:00

導(dǎo)語(yǔ):對(duì)象池技術(shù)原理分析論文一文來源于網(wǎng)友上傳,不代表本站觀點(diǎn),若需要原創(chuàng)文章可咨詢客服老師,歡迎參考。

對(duì)象池技術(shù)原理分析論文

摘要本文在分析對(duì)象技術(shù)基本原理的基礎(chǔ)上,給出了對(duì)象池技術(shù)的兩種實(shí)現(xiàn)方式。還指出了使用對(duì)象池技術(shù)時(shí)所應(yīng)注意的問題。

關(guān)鍵詞對(duì)象池;對(duì)象池技術(shù);Java對(duì)象;性能

1Java對(duì)象的生命周期分析

Java對(duì)象的生命周期大致包括三個(gè)階段:對(duì)象的創(chuàng)建,對(duì)象的使用,對(duì)象的清除。因此,對(duì)象的生命周期長(zhǎng)度可用如下的表達(dá)式表示:T=T1+T2+T3。其中T1表示對(duì)象的創(chuàng)建時(shí)間,T2表示對(duì)象的使用時(shí)間,而T3則表示其清除時(shí)間。由此,我們可以看出,只有T2是真正有效的時(shí)間,而T1、T3則是對(duì)象本身的開銷。下面再看看T1、T3在對(duì)象的整個(gè)生命周期中所占的比例。

我們知道,Java對(duì)象是通過構(gòu)造函數(shù)來創(chuàng)建的,在這一過程中,該構(gòu)造函數(shù)鏈中的所有構(gòu)造函數(shù)也都會(huì)被自動(dòng)調(diào)用。另外,默認(rèn)情況下,調(diào)用類的構(gòu)造函數(shù)時(shí),Java會(huì)把變量初始化成確定的值:所有的對(duì)象被設(shè)置成null,整數(shù)變量(byte、short、int、long)設(shè)置成0,float和double變量設(shè)置成0.0,邏輯值設(shè)置成false。所以用new關(guān)鍵字來新建一個(gè)對(duì)象的時(shí)間開銷是很大的,如表1所示。

表1一些操作所耗費(fèi)時(shí)間的對(duì)照表

運(yùn)算操作示例標(biāo)準(zhǔn)化時(shí)間

本地賦值i=n1.0

實(shí)例賦值this.i=n1.2

方法調(diào)用Funct()5.9

新建對(duì)象NewObject()980

新建數(shù)組Newint[10]3100

從表1可以看出,新建一個(gè)對(duì)象需要980個(gè)單位的時(shí)間,是本地賦值時(shí)間的980倍,是方法調(diào)用時(shí)間的166倍,而若新建一個(gè)數(shù)組所花費(fèi)的時(shí)間就更多了。

再看清除對(duì)象的過程。我們知道,Java語(yǔ)言的一個(gè)優(yōu)勢(shì),就是Java程序員勿需再像C/C++程序員那樣,顯式地釋放對(duì)象,而由稱為垃圾收集器(GarbageCollector)的自動(dòng)內(nèi)存管理系統(tǒng),定時(shí)或在內(nèi)存凸現(xiàn)出不足時(shí),自動(dòng)回收垃圾對(duì)象所占的內(nèi)存。凡事有利總也有弊,這雖然為Java程序設(shè)計(jì)者提供了極大的方便,但同時(shí)它也帶來了較大的性能開銷。這種開銷包括兩方面,首先是對(duì)象管理開銷,GC為了能夠正確釋放對(duì)象,它必須監(jiān)控每一個(gè)對(duì)象的運(yùn)行狀態(tài),包括對(duì)象的申請(qǐng)、引用、被引用、賦值等。其次,在GC開始回收“垃圾”對(duì)象時(shí),系統(tǒng)會(huì)暫停應(yīng)用程序的執(zhí)行,而獨(dú)自占用CPU。

因此,如果要改善應(yīng)用程序的性能,一方面應(yīng)盡量減少創(chuàng)建新對(duì)象的次數(shù);同時(shí),還應(yīng)盡量減少T1、T3的時(shí)間,而這些均可以通過對(duì)象池技術(shù)來實(shí)現(xiàn)。

2對(duì)象池技術(shù)的基本原理

對(duì)象池技術(shù)基本原理的核心有兩點(diǎn):緩存和共享,即對(duì)于那些被頻繁使用的對(duì)象,在使用完后,不立即將它們釋放,而是將它們緩存起來,以供后續(xù)的應(yīng)用程序重復(fù)使用,從而減少創(chuàng)建對(duì)象和釋放對(duì)象的次數(shù),進(jìn)而改善應(yīng)用程序的性能。事實(shí)上,由于對(duì)象池技術(shù)將對(duì)象限制在一定的數(shù)量,也有效地減少了應(yīng)用程序內(nèi)存上的開銷。

實(shí)現(xiàn)一個(gè)對(duì)象池,一般會(huì)涉及到如下的類:

1)對(duì)象池工廠(ObjectPoolFactory)類

該類主要用于管理相同類型和設(shè)置的對(duì)象池(ObjectPool),它一般包含如下兩個(gè)方法:

·createPool:用于創(chuàng)建特定類型和設(shè)置的對(duì)象池;

·destroyPool:用于釋放指定的對(duì)象池;

同時(shí)為保證ObjectPoolFactory的單一實(shí)例,可以采用Singleton設(shè)計(jì)模式,見下述getInstance方法的實(shí)現(xiàn):

publicstaticObjectPoolFactorygetInstance(){

if(poolFactory==null){

poolFactory=newObjectPoolFactory();

}

returnpoolFactory;

}

2)參數(shù)對(duì)象(ParameterObject)類

該類主要用于封裝所創(chuàng)建對(duì)象池的一些屬性參數(shù),如池中可存放對(duì)象的數(shù)目的最大值(maxCount)、最小值(minCount)等。

3)對(duì)象池(ObjectPool)類

用于管理要被池化對(duì)象的借出和歸還,并通知PoolableObjectFactory完成相應(yīng)的工作。它一般包含如下兩個(gè)方法:

·getObject:用于從池中借出對(duì)象;

·returnObject:將池化對(duì)象返回到池中,并通知所有處于等待狀態(tài)的線程;

4)池化對(duì)象工廠(PoolableObjectFactory)類

該類主要負(fù)責(zé)管理池化對(duì)象的生命周期,就簡(jiǎn)單來說,一般包括對(duì)象的創(chuàng)建及銷毀。該類同ObjectPoolFactory一樣,也可將其實(shí)現(xiàn)為單實(shí)例。

3通用對(duì)象池的實(shí)現(xiàn)

對(duì)象池的構(gòu)造和管理可以按照多種方式實(shí)現(xiàn)。最靈活的方式是將池化對(duì)象的Class類型在對(duì)象池之外指定,即在ObjectPoolFactory類創(chuàng)建對(duì)象池時(shí),動(dòng)態(tài)指定該對(duì)象池所池化對(duì)象的Class類型,其實(shí)現(xiàn)代碼如下:

...

publicObjectPoolcreatePool(ParameterObjectparaObj,

ClassclsType){

returnnewObjectPool(paraObj,clsType);

}

...

其中,paraObj參數(shù)用于指定對(duì)象池的特征屬性,clsType參數(shù)則指定了該對(duì)象池所存放對(duì)象的類型。對(duì)象池(ObjectPool)創(chuàng)建以后,下面就是利用它來管理對(duì)象了,具體實(shí)現(xiàn)如下:

publicclassObjectPool{

privateParameterObjectparaObj;//該對(duì)象池的屬性參數(shù)對(duì)象

privateClassclsType;//該對(duì)象池中所存放對(duì)象的類型

privateintcurrentNum=0;//該對(duì)象池當(dāng)前已創(chuàng)建的對(duì)象數(shù)目

privateObjectcurrentObj;//該對(duì)象池當(dāng)前可以借出的對(duì)象

privateVectorpool;//用于存放對(duì)象的池

publicObjectPool(ParameterObjectparaObj,ClassclsType){

this.paraObj=paraObj;

this.clsType=clsType;

pool=newVector();

}

publicObjectgetObject(){

if(pool.size()<=paraObj.getMinCount()){

if(currentNum<=paraObj.getMaxCount()){

//如果當(dāng)前池中無對(duì)象可用,而且已創(chuàng)建的對(duì)象數(shù)目小于所限制的最大值,就利用

//PoolObjectFactory創(chuàng)建一個(gè)新的對(duì)象

PoolableObjectFactoryobjFactory=

PoolableObjectFactory.getInstance();

currentObj=objFactory.createObject(clsType);

currentNum++;

}else{

//如果當(dāng)前池中無對(duì)象可用,而且所創(chuàng)建的對(duì)象數(shù)目已達(dá)到所限制的最大值,

//就只能等待其它線程返回對(duì)象到池中

synchronized(this){

try{

wait();

}catch(InterruptedExceptione){

System.out.println(e.getMessage());

e.printStackTrace();

}

currentObj=pool.firstElement();

}

}

}else{

//如果當(dāng)前池中有可用的對(duì)象,就直接從池中取出對(duì)象

currentObj=pool.firstElement();

}

returncurrentObj;

}

publicvoidreturnObject(Objectobj){

//確保對(duì)象具有正確的類型

if(obj.isInstance(clsType)){

pool.addElement(obj);

synchronized(this){

notifyAll();

}

}else{

thrownewIllegalArgumentException("該對(duì)象池不能存放指定的對(duì)象類型");

}

}

}

從上述代碼可以看出,ObjectPool利用一個(gè)java.util.Vector作為可擴(kuò)展的對(duì)象池,并通過它的構(gòu)造函數(shù)來指定池化對(duì)象的Class類型及對(duì)象池的一些屬性。在有對(duì)象返回到對(duì)象池時(shí),它將檢查對(duì)象的類型是否正確。當(dāng)對(duì)象池里不再有可用對(duì)象時(shí),它或者等待已被使用的池化對(duì)象返回池中,或者創(chuàng)建一個(gè)新的對(duì)象實(shí)例。不過,新對(duì)象實(shí)例的創(chuàng)建并不在ObjectPool類中,而是由PoolableObjectFactory類的createObject方法來完成的,具體實(shí)現(xiàn)如下:

...

publicObjectcreateObject(ClassclsType){

Objectobj=null;

try{

obj=clsType.newInstance();

}catch(Exceptione){

e.printStackTrace();

}

returnobj;

}

...

這樣,通用對(duì)象池的實(shí)現(xiàn)就算完成了,下面再看看客戶端(Client)如何來使用它,假定池化對(duì)象的Class類型為StringBuffer:

...

//創(chuàng)建對(duì)象池工廠

ObjectPoolFactorypoolFactory=ObjectPoolFactory.getInstance();

//定義所創(chuàng)建對(duì)象池的屬性

ParameterObjectparaObj=newParameterObject(2,1);

//利用對(duì)象池工廠,創(chuàng)建一個(gè)存放StringBuffer類型對(duì)象的對(duì)象池

ObjectPoolpool=poolFactory.createPool(paraObj,StringBuffer.class);

//從池中取出一個(gè)StringBuffer對(duì)象

StringBufferbuffer=(StringBuffer)pool.getObject();

//使用從池中取出的StringBuffer對(duì)象

buffer.append("hello");

System.out.println(buffer.toString());

...

可以看出,通用對(duì)象池使用起來還是很方便的,不僅可以方便地避免頻繁創(chuàng)建對(duì)象的開銷,而且通用程度高。但遺憾的是,由于需要使用大量的類型定型(cast)操作,再加上一些對(duì)Vector類的同步操作,使得它在某些情況下對(duì)性能的改進(jìn)非常有限,尤其對(duì)那些創(chuàng)建周期比較短的對(duì)象。

4專用對(duì)象池的實(shí)現(xiàn)

由于通用對(duì)象池的管理開銷比較大,某種程度上抵消了重用對(duì)象所帶來的大部分優(yōu)勢(shì)。為解決該問題,可以采用專用對(duì)象池的方法。即對(duì)象池所池化對(duì)象的Class類型不是動(dòng)態(tài)指定的,而是預(yù)先就已指定。這樣,它在實(shí)現(xiàn)上也會(huì)較通用對(duì)象池簡(jiǎn)單些,可以不要ObjectPoolFactory和PoolableObjectFactory類,而將它們的功能直接融合到ObjectPool類,具體如下(假定被池化對(duì)象的Class類型仍為StringBuffer,而用省略號(hào)表示的地方,表示代碼同通用對(duì)象池的實(shí)現(xiàn)):

publicclassObjectPool{

privateParameterObjectparaObj;//該對(duì)象池的屬性參數(shù)對(duì)象

privateintcurrentNum=0;//該對(duì)象池當(dāng)前已創(chuàng)建的對(duì)象數(shù)目

privateStringBuffercurrentObj;//該對(duì)象池當(dāng)前可以借出的對(duì)象

privateVectorpool;//用于存放對(duì)象的池

publicObjectPool(ParameterObjectparaObj){

this.paraObj=paraObj;

pool=newVector();

}

publicStringBuffergetObject(){

if(pool.size()<=paraObj.getMinCount()){

if(currentNum<=paraObj.getMaxCount()){

currentObj=newStringBuffer();

currentNum++;

}

...

}

returncurrentObj;

}

publicvoidreturnObject(Objectobj){

//確保對(duì)象具有正確的類型

if(StringBuffer.isInstance(obj)){

...

}

}

5結(jié)束語(yǔ)

恰當(dāng)?shù)厥褂脤?duì)象池技術(shù),能有效地改善應(yīng)用程序的性能。目前,對(duì)象池技術(shù)已得到廣泛的應(yīng)用,如對(duì)于網(wǎng)絡(luò)和數(shù)據(jù)庫(kù)連接這類重量級(jí)的對(duì)象,一般都會(huì)采用對(duì)象池技術(shù)。但在使用對(duì)象池技術(shù)時(shí)也要注意如下問題:

·并非任何情況下都適合采用對(duì)象池技術(shù)。基本上,只在重復(fù)生成某種對(duì)象的操作成為影響性能的關(guān)鍵因素的時(shí)候,才適合采用對(duì)象池技術(shù)。而如果進(jìn)行池化所能帶來的性能提高并不重要的話,還是不采用對(duì)象池化技術(shù)為佳,以保持代碼的簡(jiǎn)明。

·要根據(jù)具體情況正確選擇對(duì)象池的實(shí)現(xiàn)方式。如果是創(chuàng)建一個(gè)公用的對(duì)象池技術(shù)實(shí)現(xiàn)包,或需要在程序中動(dòng)態(tài)指定所池化對(duì)象的Class類型時(shí),才選擇通用對(duì)象池。而大部分情況下,采用專用對(duì)象池就可以了。

參考文獻(xiàn)

[1]BruceEckel著,京京工作室譯,Java編程思想【M】,機(jī)械工業(yè)出版社,2003年1月