Утечка памяти в JdbcRowSet

Я пытаюсь заполнить JdbcRowSet записями из большой таблицы (около десяти тысяч записей). Я пробовал два варианта (см. код ниже):

  1. Создайте объект подключения, создайте экземпляр с помощью JdbcRowSetImpl (соединение), выполните запрос в цикле.
  2. Создайте экземпляр с помощью JdbcRowSetImpl(DriverManager.getConnection("jdbc:...."), выполните запрос в цикле.

Первый вариант приводит к утечке памяти до тех пор, пока куча не будет заполнена. Второй вариант не имеет утечки памяти. Может кто-нибудь объяснить мне, почему первое вызывает утечку памяти при повторном использовании объекта соединения?

Благодарность

Код для 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();
        }
    }

}

}

Код на 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();
        }
    }

}

}

person Buehlmann    schedule 12.09.2013    source источник
comment
Вы закрываете ресурс во втором, а не в первом фрагменте кода, это имеет значение, не так ли?   -  person kosa    schedule 12.09.2013
comment
Если вы не закроете ResultSet и Statement (и даже Connection, если он объединен) после каждого запроса, вы получите утечку. Как удачно показано здесь. //jdbcRS.close(); <-- Returns a null pointer Exception. Хм...   -  person Boris the Spider    schedule 12.09.2013
comment
Да, Борис, вы правы, но у меня вопрос, почему я не могу закрыть ResultSet в первом снипсете, а во втором?   -  person Buehlmann    schedule 16.09.2013


Ответы (1)


Ответ на ваш вопрос 'почему первое вызывает утечку памяти при повторном использовании объекта соединения?'

Да, вы повторно используете объект подключения, но вы создаете новый объект JdbcRowSet на каждой итерации и не закрываете его, что приводит к утечке памяти. jdbcRS = null; не закрывает ресурс.

Ответ на ваш вопрос 'почему я не могу закрыть ResultSet в первом фрагменте, но во втором?'

В первом фрагменте кода, когда вы закрываете JdbcRowSet объект jdbcRS с помощью метода close , jdbcRS.close(); вы также закрываете соединение. Таким образом, вторая итерация вызовет исключение NullPointerException, потому что в jdbcRS = new JdbcRowSetImpl(connection); connection уже закрыто.

Второй фрагмент кода работает нормально, потому что вы создаете новое соединение на каждой итерации в методе getConnection.

Лучше всего было бы, если бы вы использовали CachedRowSet для автоматического закрытия ресурса после каждой итерации:

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();
        }
    }
}
person Laz London    schedule 01.06.2014