Proxool是一個很優秀的開源串連池。我曾經比較過dbcp, c3p0 和 proxool 這三個串連池,閱讀和分析它們的代碼。相比之下,proxool 用了cglib,其源碼顯得相當簡潔優雅。
但是很不幸,在這一次,記憶體流失的帳要算在它頭上。 我們使用的是proxool 的0.9.1的版本,環境是Oracle JDBC5 + IBM JDK5。
0.9.1版本是proxool 的最新版本。我們有一個應用據稱運行以來,就一直處於記憶體溢出的陰霾之中。追趕資料時的某些巨忙的系統可能會一天內死好些次,甚至達十幾次。不得不採用一些監控指令碼定時重啟。去年12月我接手以來,也被這個問題困擾,最糟糕的時候甚至某些硬體設施很好的部署執行個體的多達50+GB的堆也能在一天之內吃光。
出錯的原因很簡單,proxool使用了cglib,它用WrappedConnection代理實際的Conneciton。在運行WrappedConnection的方法時,包括其finalize方法,都會調用Conneciton.isClosed()方法去判斷是否真的需要執行某些操作。不幸的是Oracle
JDBC中的這個方法是同步的,鎖是連線物件本身。於是, Finalizer線程回收剛執行過的WrappedConnection對象時就總會與還在使用Connection的各個背景工作執行緒爭用鎖。
貼一張簡單的圖,你就懂了:
解決方案是在WrappedConnection.java的第114行,做一點小小的改動,在調用的方法為finalize方法時,不要去掉isClosed()方法。事實上,WrappedConnection#finalize()方法的調用與Connection根本沒啥關係。Oracle JDBC本身沒有實現finalize()方法,是cglib代理的對象自己產生了一個finalize()方法而已。
修改前的114行:
if (proxyConnection != null && proxyConnection.isReallyClosed()) {
修改後的114行:
if (proxyConnection != null && !concreteMethod.getName().equals(FINALIZE_METHOD) && proxyConnection.isReallyClosed()) {
點此查看sourceforge上的完整的WrappedConnection.java。
效果是很明顯的。一方面,記憶體消耗明顯降低。之前,所有的部署執行個體都會隔段時間就當掉,某些部署執行個體的52G的堆都會溢出!現在,系統再忙其堆大小也基本上在原來那個設得老大的Xms參數之下。 第二方面,輸送量忽然提高了許多,沒有煩人的回收線程(其優先順序要高點)的幹擾,當然應該要跑得更快些。
感興趣的朋友可以下載我這裡提供的jar包(-target 1.5),(源碼是直接從官網下的proxool-0.9.1-source.zip)。這個jar包也解決了ProxoolDataSource在Spring中作為bean配置時有三個屬性(maximumConnectionLifetime,houseKeepingSleepTime,overloadWithoutRefusalLifetime)不能注入的問題。詳細請看這篇博文。
下載 proxool-0.9.1
也可以應用patch自己編譯一個。patch我上傳到Bug頁面了,請去那下載。
轉自:http://blog.xiping.me/2011/04/proxool-memory-leak.html