對象池作為全局資源,高并發(fā)環(huán)境中多個(gè)線程可能同時(shí)需要獲取對象池的對象,因此多個(gè)線程在爭搶對象時(shí)因?yàn)殒i競爭而阻塞,因此使用對象池有線程同步的開銷,而不使用對象池則有創(chuàng)建和銷毀對象的開銷。對于對象池本身的設(shè)計(jì)來說,需要盡量做到無鎖化,比如Jetty就使用了ConcurrentLinkedDeque。如果你的內(nèi)存足夠大,可以考慮線程本地對象池,這樣每個(gè)線程都有自己的對象池,線程之間互不干擾。
為了防止對象池的無限膨脹,必須要對池的大小做限制。對象池太小發(fā)揮不了作用,對象池太大的話可能有空閑對象,這些空閑對象會一直占用內(nèi)存,造成內(nèi)存浪費(fèi)。這里你需要根據(jù)實(shí)際情況做一個(gè)平衡,因此對象池本身除了應(yīng)該有自動(dòng)擴(kuò)容的功能,還需要考慮自動(dòng)縮容。
所有的池化技術(shù),包括緩存,都會面臨內(nèi)存泄露的問題,原因是對象池或者緩存的本質(zhì)是一個(gè)Java集合類,比如List和Stack,這個(gè)集合類持有緩存對象的引用,只要集合類不被GC,緩存對象也不會被GC。維持大量的對象也比較占用內(nèi)存空間,所以必要時(shí)我們需要主動(dòng)清理這些對象。以Java的線程ThreadPoolExecutor
為例,它提供了allowCoreThreadTimeOut和setKeepAliveTime兩種方法,可以在超時(shí)后銷毀線程,我們在實(shí)際項(xiàng)目中也可以參考這個(gè)策略。
另外在使用對象池時(shí),我這里還有一些小貼士供你參考:
- 對象在用完后,需要調(diào)用對象池的方法將對象歸還給對象池。
- 對象池中的對象在再次使用時(shí)需要重置,否則會產(chǎn)生臟對象,臟對象可能持有上次使用的引用,導(dǎo)致內(nèi)存泄漏等問題,并且如果臟對象下一次使用時(shí)沒有被清理,程序在運(yùn)行過程中會發(fā)生意想不到的問題。
- 對象一旦歸還給對象池,使用者就不能對它做任何操作了。
- 向?qū)ο蟪卣埱髮ο髸r(shí)有可能出現(xiàn)的阻塞、異?;蛘叻祷豱ull值,這些都需要我們做一些額外的處理,來確保程序的正常運(yùn)行。