Resolvendo ORA-01000: maximum open cursors exceeded

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 Connections, Statements e ResultSets 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 PreparedStatements sejam usados ao invés de concatenar Strings 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 ;-)

15 respostas para “ Resolvendo ORA-01000: maximum open cursors exceeded ”

  1. Bruno disse:

    pq serah que voce postou isso heim?? por que serah?!?!

    aheuaUAEHuHEUhauaHEUhaEUheauhea

  2. Michael Nascimento Santos disse:

    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.

  3. Marcelo Tiziano disse:

    Valeu pela dica, já tive o memso problema e esta dica me ajudou a ir direto no problema.

  4. Wellington disse:

    Estava com este problema e consegui contorná-lo, valeu!!!

  5. Jairo de Souza disse:

    realmente nao é facil encontrar dicas sobre isso no google.. vc me ajudou bastante.

  6. Gian disse:

    Boa parabens! me ajudou mto .

    um abraço

  7. Thiago Ananias disse:

    Cara! Show, estava atraz disso mesmo! []s

  8. Sérgio Berlotto disse:

    Realmente muito boa a dica !
    Bem explicada, prática e muito útil !

  9. César Melchior disse:

    Parabéns pela dica. Realmente muito útil.

    César

  10. Giovani Dal Piva disse:

    Muito boa dica!!! … Estava com esse problema e lendo aqui consegui resolve-lo …

    Vlw

  11. Mário disse:

    Muito boa dica. Descobri este problema somente após mandar a plicação pra homologação, pois usavamos jboss no desenvolvimento, e websphere na homologação. o Jboss fechava as conexões abertas após um tempo, o websphere não.

  12. Marcos disse:

    Cara, show!!!!!

    vlw!!!!

  13. Carlos Eduardo disse:

    Caraca, funcionou mesmo, nunca iria imaginar que fazendo ajustes no try catch poderia resolver esse problema.

    Obrigadão Mestre, me salvou de um pepino aqui na empresa.

    Um grande abraço.

  14. Lucas A.F. disse:

    Tenho uma duvida…
    quando ocorre o erro, tem como resolver direto no banco?
    sem ter que alterar a aplicaçao?

    ex.: O cliente possui uma nota com mais de 100 itens, quando vai gravar da o erro. Tem como resolver sem que o cliente feche a aplicaçao ou cancele a operaçao?

  15. Marcos Vinícius disse:

    Muito bom, agora eu já descobri qual sql/cursor não foi fechado, mas…. e agora? Como fecha-los?

Deixe uma resposta.