Bug #63284 Memory leak with Failover proxied Statement/PreparedStatement with DBCP
Submitted: 16 Nov 2011 11:37 Modified: 24 Feb 2012 9:08
Reporter: Dennis Cheung Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.1.15 OS:Any
Assigned to: CPU Architecture:Any

[16 Nov 2011 11:37] Dennis Cheung
Description:
Universal proxy were generated for all JDBC return with a Failover Connection.
And their "equals" method are not implemented.

DBCP use ArrayList, which use "equals" method. Because of Proxy object always return false as real object != proxy. (Ref: www.javaspecialists.eu/archive/Issue126.html)

As a result, following stack could happen with use of DBCP, due the growing amount of elements in AbandonedTrace's ArrayList.
"main" prio=10 tid=0x000000004fad9800 nid=0x338c runnable [0x0000000041e6c000]
   java.lang.Thread.State: RUNNABLE
        at com.mysql.jdbc.LoadBalancingConnectionProxy$ConnectionErrorFiringInvocationHandler.invoke(LoadBalancingConnectionProxy.java:106)
        at com.mysql.jdbc.FailoverConnectionProxy$FailoverInvocationHandler.invoke(FailoverConnectionProxy.java:51)
        at $Proxy9.equals(Unknown Source)
        at org.apache.commons.dbcp.DelegatingPreparedStatement.equals(DelegatingPreparedStatement.java:77)
        at java.util.ArrayList.remove(ArrayList.java:423)
        at org.apache.commons.dbcp.AbandonedTrace.removeTrace(AbandonedTrace.java:225)
        - locked <0x00000000f0537eb0> (a org.apache.commons.dbcp.PoolableConnection)
        at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:151)
        at org.springframework.jdbc.support.JdbcUtils.closeStatement(JdbcUtils.java:87)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:608)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:636)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:665)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:673)
        at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:733)
 ....

The memory leak also causing high CPU usage.

How to repeat:
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
                <property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <property name="url" value="=jdbc:mysql://a:3306,b:3306,c:3306" />
                <property name="minEvictableIdleTimeMillis" value="50000" />
                <property name="timeBetweenEvictionRunsMillis" value="50000" />
                <property name="numTestsPerEvictionRun" value="3" />
                <property name="defaultAutoCommit" value="true" />
                <property name="maxActive" value="1" />
                <property name="maxIdle" value="1" />
                <property name="defaultReadOnly" value="true" />
        </bean>

while(true){
  PreparedStatement pstmt=con.prepareStatement("SELECT 1");
  JdbcUtils.closeStatement(stmt);
}

Suggested fix:
Replace proxy with a subclass. Or, implement the equals correctly.
[29 Nov 2011 14:17] Tonci Grgin
Hi Dennis and thanks for your report.

Verified as described. As Todd informs me, this is a side-effect of patch for Bug#48442.
[24 Feb 2012 9:08] Tonci Grgin
Pushed up to revision 1121.
[23 Mar 2012 1:33] John Russell
Added to changelog for 5.1.19: 

A combination of failover connections, proxied or prepared
statements, and database connection pool could cause a memory leak
due to improper implementation of equals().