Junto com o clássico OutOfMemoryError
, este é um dos erros que mais aparece em sistemas que usam Oracle. Resolver esse problema não é difícil, como vou explicar aqui.
Este erro é causado por Connection
s, Statement
s e ResultSet
s deixados abertos. Normalmente temos algo como:
Connection con = //obtem conexao; PreparedStatement ps = con.prepareStatement("SELECT ..."); ResultSet rs = ps.executeQuery(); while (rs.next()) { // ... } rs.close(); ps.close(); con.close();
Note que eu estou sendo otimista, supondo que PreparedStatement
s sejam usados ao invés de concatenar String
s para executar consultas e que o método close()
nas três instâncias esteja pelo menos no código. Se você não fez nem isso, corra atrás. :-)
O bug é que, quando ocorre uma exceção, durante o processamento do select, nenhum dos três objetos é fechado. Então, a primeira solução é colocar o fechamento num bloco try
/finally
, como mostrado abaixo:
Connection con = null; PreparedStatement ps = null; ResultSet rs =null; try { con = //obtem conexao; ps = con.prepareStatement("SELECT ..."); rs = ps.executeQuery(); while (rs.next()) { // ... } } finally { if (rs != null) { rs.close(); } if (ps != null) { ps.close(); } if (con != null) { con.close(); } }
Tudo certo, né? Errado. Se ocorrer uma exceção ao fechar rs
, ps
e con
nunca serão fechados. Então a melhor solução seria:
Connection con = null; PreparedStatement ps = null; ResultSet rs =null; try { con = //obtem conexao; ps = con.prepareStatement("SELECT ..."); rs = ps.executeQuery(); while (rs.next()) { // ... } } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqe) { // logar excecao } catch (RuntimeException re) { // logar excecao } } if (ps != null) { try { ps.close(); } catch (SQLException sqe) { // logar excecao } catch (RuntimeException re) { // logar excecao } } if (con != null) { try { con.close(); } catch (SQLException sqe) { // logar excecao } catch (RuntimeException re) { // logar excecao } } }
Ok, você vai me dizer, muito legal isso. Mas meu projeto tem milhares de classes e eu não sei onde eu esqueci de fazer isso. Ou, ainda, eu uso um framework O/RM ou JDBC que deveria resolver este problema pra mim. Bem, nesse caso, você tem como descobrir qual a instrução SQL do(s) cursor(es) que está(ão) abertos. Primeiro, você precisa do SID das sessões abertas pela sua aplicação, que você pode obter assim:
select o.sid, osuser, machine, count(*) num_curs from v$open_cursor o, v$session s where o.sid=s.sid group by o.sid, osuser, machine order by num_curs;
Se estiver usando connection pool e um máquina só, pode-se usar estes dados para filtrar os resultados:
select o.sid, osuser, machine, count(*) num_curs from v$open_cursor o, v$session s where o.sid=s.sid and user_name = 'USUARIO_BANCO' and machine = 'NOME_MAQUINA_APLICACAO' group by o.sid, osuser, machine order by o.sid;
Utilize os SIDs na seguinte query:
select q.sql_text, count(*) from v$open_cursor o, v$sql q where q.hash_value=o.hash_value and o.sid IN (<SIDs>) group by q.sql_text order by 2;
Se o framework ou servidor de aplicações que você utiliza faz cache de statements (Hibernate e JBoss fazem, por default), é normal que exista um certo número de cursores com o mesmo SQL em aberto. Suspeite daquelas instruções que tenham um número de cursores muito maior que as outras e investigue de onde elas vêm. E se sua aplicação usa PL/SQL, saiba que as instruções contidas nas suas procedures/functions são contadas individualmente como cursores. Espero ter ajudado ;-)
pq serah que voce postou isso heim?? por que serah?!?!
aheuaUAEHuHEUhauaHEUhaEUheauhea
Digamos que foi uma homenagem a um amigo :-)
Agora, falando sério, a razão de eu ter postado é porque esse erro é muito freqüente e achar no Google um guia de como diagnosticar e resolver o problema não é exatamente muito fácil.
Valeu pela dica, já tive o memso problema e esta dica me ajudou a ir direto no problema.
Estava com este problema e consegui contorná-lo, valeu!!!
realmente nao é facil encontrar dicas sobre isso no google.. vc me ajudou bastante.
Boa parabens! me ajudou mto .
um abraço
Cara! Show, estava atraz disso mesmo! []s
Realmente muito boa a dica !
Bem explicada, prática e muito útil !
Parabéns pela dica. Realmente muito útil.
César
BOA!