ablog

不器用で落着きのない技術者のメモ

カーソルが解放されるタイミング (2)

カーソルが解放されるタイミング - ablog の続き。
Statement、ResultSet を毎回 close しながら無限ループするプログラムと、Statement、ResultSet を close せずに無限ループするプログラムを実行してみて、前者は永久に実行され、後者はカーソルリークで例外が発生して異常終了することを検証してみた。


[結論]
Statement を close すればカーソルリークは発生しない。コネクションプーリングを使わない場合は、Connection を close するとカーソルリークは発生しないが、コネクションプーリングを使う場合は Connection を close しても、Statement を close しないとカーソルリークが発生する。たぶん。

やさしく学ぶ基礎からのJDBC P.137

java.sql.Statementをクローズすると、自動的に(連鎖的に)クローズされるようにJDBC APIの仕様で定められています。
...
昔のバージョンのOracle JDBCドライバは、java.sql.ResultSetを明示的にクローズしないと問題が発生する不具合があった。

ということで、ResultSet も close しておいたほうが良いみたい。


[検証結果]
まず、Statement、ResultSet を毎回 close しながら無限ループするプログラムを実行してみる。

$ java InfiniteLooperWithCursorClosed
001 scott
...
001 scott
^C

次に、Statement、ResultSet を close せずに無限ループするプログラムを実行してみる。

$ java InfiniteLooperWithCursorOpened
001 scott
...
001 scott
Error code: 1000
SQL state: 72000
java.sql.SQLException: ORA-01000: maximum open cursors exceeded

	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
	at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:745)
	at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210)
	at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:804)
	at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1049)
	at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:845)
	at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1154)
	at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1313)
	at InfiniteLooperWithCursorOpened.main(InfiniteLooperWithCursorOpened.java:18)

想定通りの結果。
追加で、Statement だけ close した場合、 ReslutSet のみ close した場合にそれぞれどうなるか検証してみた。

ResultSet をclose せずに Statement だけ close しても、カーソルリークしない。

$ java InfiniteLooperWithStmtClosed
001 scott
...
001 scott
^C

Statement をclose せずに ResultSet だけ close すると、カーソルリークした。

$ java InfiniteLooperWithRsClosed
001 scott
...
001 scott
Error code: 1000
SQL state: 72000
java.sql.SQLException: ORA-01000: maximum open cursors exceeded

	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
	at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:745)
	at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210)
	at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:804)
	at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1049)
	at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:845)
	at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1154)
	at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1313)
	at InfiniteLooperWithRsClosed.main(InfiniteLooperWithRsClosed.java:18)
$ java InfiniteLooperWithRsClosed


[検証用プログラムのソースコード]

  • InfiniteLooperWithCursorClosed.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InfiniteLooperWithCursorClosed {
        public static void main(String args[]) {
                Connection conn = null;
                Statement stmt = null;
                ResultSet resultSet = null;
                try {
                        Class.forName ("oracle.jdbc.driver.OracleDriver");
                        conn = DriverManager.getConnection
                                ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger");
                        for(;;) {
                            stmt = conn.createStatement();
                            resultSet = stmt.executeQuery("select id, name from emp");
                            for(;resultSet.next();) {
                                System.out.println(resultSet.getString(1) + " " + resultSet.getString(2));
                            }
                            try {
                                if (resultSet != null) {
                                resultSet.close();
                            }
                                } catch (SQLException e){
                                e.printStackTrace();
                            }
                            try {
                                if (stmt != null) {
                                        stmt.close();
                                }
                            } catch (SQLException e){
                                e.printStackTrace();
                            }
                        }
                } catch (SQLException e) {
                        System.out.println("Error code: " + e.getErrorCode());
                        System.out.println("SQL state: " + e.getSQLState());
                        e.printStackTrace();
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                } finally {
                        try {
                                if (resultSet != null) {
                                        resultSet.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                        try {
                                if (stmt != null) {
                                        stmt.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                        try {
                                if (conn != null) {
                                        conn.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                }
        }
}
  • InfiniteLooperWithCursorOpened.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InfiniteLooperWithCursorOpened {
        public static void main(String args[]) {
                Connection conn = null;
                Statement stmt = null;
                ResultSet resultSet = null;
                try {
                        Class.forName ("oracle.jdbc.driver.OracleDriver");
                        conn = DriverManager.getConnection
                                ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger");
                        for(;;) {
                            stmt = conn.createStatement();
                            resultSet = stmt.executeQuery("select id, name from emp");
                            for(;resultSet.next();) {
                                System.out.println(resultSet.getString(1) + " " + resultSet.getString(2));
                            }
                        }
                } catch (SQLException e) {
                        System.out.println("Error code: " + e.getErrorCode());
                        System.out.println("SQL state: " + e.getSQLState());
                        e.printStackTrace();
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                } finally {
                        try {
                                if (resultSet != null) {
                                        resultSet.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                        try {
                                if (stmt != null) {
                                        stmt.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                        try {
                                if (conn != null) {
                                        conn.close();
                                }
                        } catch (SQLException e){
                                e.printStackTrace();
                        }
                }
        }
}
  • InfiniteLooperWithStmtClosed.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InfiniteLooperWithStmtClosed {
	public static void main(String args[]) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet resultSet = null;
		try {
			Class.forName ("oracle.jdbc.driver.OracleDriver");
			conn = DriverManager.getConnection
				("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger");
			for(;;) {
			    stmt = conn.createStatement();
			    resultSet = stmt.executeQuery("select id, name from emp");
			    for(;resultSet.next();) {
				System.out.println(resultSet.getString(1) + " " + resultSet.getString(2));
			    }
			    /*
    		            try {
				if (resultSet != null) {
		  		resultSet.close();
			    }
				} catch (SQLException e){
				e.printStackTrace();
			    }
			    */
			    try {
				if (stmt != null) {
		  			stmt.close();
				}
			    } catch (SQLException e){
				e.printStackTrace();
			    }
			}
       		} catch (SQLException e) {
			System.out.println("Error code: " + e.getErrorCode());
			System.out.println("SQL state: " + e.getSQLState());
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if (resultSet != null) {
		  			resultSet.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
			try {
				if (stmt != null) {
		  			stmt.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
			try {
				if (conn != null) {
		  			conn.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
       		}
	}
}
  • InfiniteLooperWithRsClosed.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InfiniteLooperWithRsClosed {
	public static void main(String args[]) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet resultSet = null;
		try {
			Class.forName ("oracle.jdbc.driver.OracleDriver");
			conn = DriverManager.getConnection
				("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger");
			for(;;) {
			    stmt = conn.createStatement();
			    resultSet = stmt.executeQuery("select id, name from emp");
			    for(;resultSet.next();) {
				System.out.println(resultSet.getString(1) + " " + resultSet.getString(2));
			    }
			    try {
				if (resultSet != null) {
		  		resultSet.close();
			    }
				} catch (SQLException e){
				e.printStackTrace();
			    }
			    /*
			    try {
				if (stmt != null) {
		  			stmt.close();
				}
			    } catch (SQLException e){
				e.printStackTrace();
			    }
			    */
			}
       		} catch (SQLException e) {
			System.out.println("Error code: " + e.getErrorCode());
			System.out.println("SQL state: " + e.getSQLState());
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if (resultSet != null) {
		  			resultSet.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
			try {
				if (stmt != null) {
		  			stmt.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
			try {
				if (conn != null) {
		  			conn.close();
				}
			} catch (SQLException e){
				e.printStackTrace();
			}
       		}
	}
}