javamysqljdbcmemory-leaksrowset

Memory Leak in JdbcRowSet


I try to populate a JdbcRowSet with records from a large table (some ten thousand records). I tried two variations (see code below):

  1. Create a connection object, Instantiate using JdbcRowSetImpl(connection), execute the query in a loop.
  2. 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();
        }
    }

}

}

Solution

  • 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. The jdbcRS = 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 object jdbcRS using the close method , jdbcRS.close(); you close the connection as well. So the second iteration will throw a NullPointerException because at jdbcRS = new JdbcRowSetImpl(connection); the connection 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:

    package databases;
    import java.sql.SQLException;
    import javax.sql.rowset.*;
    
    public class CachedRowSet_Usage {
    
        public static void main(String[] args) {
            String username = "username";
            String password = "password";
            String url = "jdbc:mysql://localhost:3306/your_database_name"; 
    
            try{
                CachedRowSet rs = RowSetProvider.newFactory().createCachedRowSet();
                //JdbcRowSet rs = RowSetProvider.newFactory().createJdbcRowSet();
                rs.setUrl(url);
                rs.setUsername(username);
                rs.setPassword(password);
    
                for(int i=0;i<150;i++){
                    System.out.println(i);
                    rs.setCommand("Select * from your_table");
                    rs.execute();  
                    //rs.close();  <-- no use, rs closes automatically
                }
            }
            catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }