Description:
I have a Java application which parses JSON messages from an ActiveMQ message queue.
The application then calls a stored procedure in the database to insert values pulled from the JSON into the database.
All of this works, but after a length of time, I see the memory consumption of my application gradually climbing. After taking a heap dump and parsing it using the Eclipse Memory analyser tool, it points to JDBC42CallableStatements as the leak suspect.
Each heap dump has greater numbers of JDBC CallableStatements etc..
There were 200,000 instances of CallableStatements and matching numbers of Resultsets, ParseInfo objects in one dump I took.
IntelliJ has a MemoryView plugin which shows that the number of CallableStatement objects in memory does not decrease after I close them. I get that they may still exist until Garbage collection but analysis of the Heap shows that these objects are never considered for destruction. There is no destroy() method, so it seems like these objects just continue to increase.
Because I'm processing potentially millions of messages, I keep a persistent Database Connection open, but I have also tried opening and closing the Connection object each time to call the stored procedure and I see the same behaviour with it.
Stepping through the code below with IntelliJ memory viewer open showed the class count increase and never decrease even after I call system.gc
https://blog.jetbrains.com/idea/2016/08/jvm-debugger-memory-view-for-intellij-idea/
I'm using the latest
ConnectorJ mysql-connector-java-5.1.42-bin
and
JDK C:\Program Files\Java\jdk1.8.0_131\bin\java
How to repeat:
Here's my code. I've tried CallableStatements declared at both the Class and function level.
private int db_callsrored proc(int someint, String somestring){
int retval=0;
String sp_name = "mydb.sp_insertdata";
String query = "{CALL " + sp_name +"(?,?,?)}";
if (dbconnected){
int igwid = 0;
l4j.info("Prepare Database Sp->" + sp_name);
try{
CallableStatement cs;
cs = dbconnection.prepareCall(query);
cs.setInt("vRVPIN", rvpin);
cs.setString("vsrtID", somestring);
cs.setInt("vintID", 0);
cs.registerOutParameter("vintID", Types.INTEGER);
//callablestatement.closeOnCompletion();
cs.executeUpdate();
l4j.info("Executed Database Sp->" + sp_name);
retval = cs.getInt("vintID");
cs.getResultSet().close();
cs.close();
cs=null;
//retval = Integer.toString(igwid);
l4j.info("Got id from Db->" + retval);
}catch(MySQLDataException e){
l4j.error("MySQLDataException Exception calling " + sp_name, e);
}catch (SQLException e) {
l4j.error("SQLException Error calling " + sp_name, e);
} catch (NullPointerException e){
l4j.error("NullPointerException Error calling " + sp_name, e);
}
}else{
l4j.error("Unable to execute " + sp_name + " - no Database connection");
}
return retval;
}