Я получаю утечку памяти, не закрывая мои JDBC PreparedStatements? - PullRequest
3 голосов
/ 19 сентября 2011

Я работаю с java.sql PreparedStatements, и мне было интересно узнать следующее:

В Java является передачей по значению, Черт! , следующее задано какПример соглашений Java Pass-By:

public void foo(Dog d) {
    d = new Dog("Fifi"); // creating the "Fifi" dog
}

Dog aDog = new Dog("Max"); // creating the "Max" dog
// at this point, aDog points to the "Max" dog
foo(aDog);
// aDog still points to the "Max" dog 

В моем коде это выглядит следующим образом (псевдокод полу-Java):

public void method() {
  PreparedStatement pstmt = null;
  ResultSet rs = null;
  try {
    rs = executeStatement(sql-string, pstmt, conn, vars...);
  } catch (....) { /* error-handling */ }
  /// do stuff with the data
  rs.close();
}

, где executeStatement - это (что-то похожее на следующее:

ResultSet executeStatement(String sql, PreparedStatement pstmt, Connection conn, Object[] vars...) {
  pstmt = conn.prepareStatement(sql);
  /// set pstmt variables...
  ResultSet rs = pstmt.execute();
  return rs;
}

Из того, что я понимаю в соглашениях о передаче Java, мне бесполезно делать что-либо с pstmt в основном коде, так как он все равно будет нулевым, даже после вызоваexecuteStatement.Однако, поскольку закрытие PreparedStatement также закрывает ResultSet, я знаю, что PreparedStatement, созданный в executeStatement, не закрывается при обработке ResultSet.

Означает ли это, что существуетутечка памяти здесь?(Мое понимание утечек памяти и того, как их можно диагностировать / исправить, в лучшем случае нечеткое).Есть ли способ, которым я мог бы структурировать это по-другому, чтобы избежать утечки, но продолжать иметь метод, который может выполнить строку SQL и вернуть ResultSet абстрактным способом?

Ответы [ 2 ]

3 голосов
/ 20 сентября 2011

Во-первых, утечки памяти в традиционном смысле, когда у вас есть выделенные данные, на которые больше нельзя ссылаться, не существуют в Java, так как собирается только то, на что нет ссылок. Тем не менее, в этом случае вы все равно ищете не утечку памяти, а утечку ресурсов (что является более серьезной проблемой в Java): память PreparedStatement будет в конечном итоге собрана, и она будет освобождена, так как она есть. однако на него больше не ссылаются после выполнения вашего метода, однако ресурсы, удерживаемые оператором, должны быть освобождены намного раньше, а не только после запуска сборщика мусора.

Что вы можете сделать, это написать класс, который содержит как Statement, так и ResultSet в качестве члена, и вернуть этот класс следующим образом:

class ResultSetStatementPair {
  ResultSetStatementPair(ResultSet rs, Statement stmt) {
    this.rs = rs; this.stmt = stmt;
  }

  ResultSet rs;
  Statement stmt;
}

ResultSetStatementPair executeStatement(String sql, Connection conn, Object[] vars...) {
  PreparedStatement pstmt = conn.prepareStatement(sql);
  ResultSet rs = pstmt.execute();
  return new ResultSetStatementPair(rs, pstmt);
}

public void method() {
  Statement pstmt = null;
  ResultSet rs = null;
  try {
    ResultSetStatementPair pair = executeStatement(sql-string, pstmt, conn, vars...);
    rs = pair.rs;
    stmt = pair.stmt;
    // do stuff with the data
  } catch (....) { /* error-handling */ }
  finally { 
    if(rs != null) rs.close();
    if(stmt != null) stmt.close();
  }
}

Также обратите внимание, что я добавил finally, а также переместил do stuff в блок try.

2 голосов
/ 20 сентября 2011

Я не вижу здесь утечки памяти. Единственная проблема, которую я нахожу в том, что вы не закрываете resultSet в блоке finally. Поэтому, если выдается исключение, rs.close() не будет выполнено.

Как прокомментировал Андрей, закрытие результирующего набора также закроет базовый оператор. Я не уверен, где вы закрываете соединение, но это также должно произойти в блоке finally.

...