I try to populate a JdbcRowSet with records from a large table (some ten thousand records). I tried two variations (see code below):
- Create a connection object, Instantiate using JdbcRowSetImpl(connection), execute the query in a loop.
- Instantiate using JdbcRowSetImpl(DriverManager.getConnection("jdbc:...."), execute the query in a loop.
The first variation results in a memory leak until the heap is full. The second variant has no memory leak. Can somebody explain me why the first causes a memory leak when reusing the connection Object?
thanks
Code for 1.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.rowset.JdbcRowSet;
import com.sun.rowset.JdbcRowSetImpl;
public class JdbcRowSetMemoryLeak {
/**
* @param args
*/
public static void main(String[] args) {
String username = "user";
String password = "password";
Connection connection = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost/db_ams?user=" + username + "&password=" + password);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
JdbcRowSet jdbcRS = null;
for (int i=0;i<150;i++){
System.out.println(i);
try {
jdbcRS = new JdbcRowSetImpl(connection); // <-- Memory is leaking
jdbcRS.setCommand("Select * from sample_t;");
jdbcRS.execute();
// jdbcRS.close(); <-- Returns a null pointer Exception
jdbcRS = null;
} catch (SQLException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Code for 2.
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.rowset.JdbcRowSet;
import com.sun.rowset.JdbcRowSetImpl;
public class JdbcRowSetMemoryGood {
/**
* @param args
*/
public static void main(String[] args) {
String username = "user";
String password = "password";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
JdbcRowSet jdbcRS = null;
for (int i=0;i<150;i++){
System.out.println(i);
try {
jdbcRS = new JdbcRowSetImpl(DriverManager.getConnection("jdbc:mysql://localhost/db_ams?user=" + username + "&password=" + password));
jdbcRS.setCommand("Select * from sample_t;");
jdbcRS.execute();
jdbcRS.close();
jdbcRS = null;
} catch (SQLException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The answer to your question 'why the first causes a memory leak when reusing the connection Object?'
Yes, you are reusing the connection object but you are creating a new
JdbcRowSet
object in every iteration and you don't close it which leads to memory leak. ThejdbcRS = null;
doesn't close the resource.The answer to your question 'why can I not close the ResultSet in the first snippset but in the second?'
In your first code snippet, when you close the
JdbcRowSet
objectjdbcRS
using the close method ,jdbcRS.close();
you close the connection as well. So the second iteration will throw a NullPointerException because atjdbcRS = new JdbcRowSetImpl(connection);
theconnection
is already closed.The second code snippet works fine because you create a new connection in every iteration in the
getConnection
method.The best would be if you used
CachedRowSet
for closing the resource after every iteration automatically: