我收到了ORA-01000 SQL异常。因此,我对此有一些疑问。
{ //method try starts String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)"; pStmt = obj.getConnection().prepareStatement(sql); pStmt.setLong(1, subscriberID); for (String language : additionalLangs) { pStmt.setInt(2, Integer.parseInt(language)); pStmt.execute(); } } //method/try ends { //finally starts pStmt.close() } //finally ends
如果在单个连接对象上多次调用conn.createStatement()和conn.prepareStatement(sql)会发生什么?
conn.createStatement()
conn.prepareStatement(sql)
Edit1: 6.使用弱/软引用语句对象是否有助于防止泄漏?
Edit2: 1.有什么办法可以在我的项目中找到所有缺少的“ statement.close()”吗?我了解这不是内存泄漏。但是我需要找到一个有资格进行垃圾回收的语句引用(不执行close())?有没有可用的工具?还是我必须手动分析它?
请帮助我理解它。
Solution
在Oracle DB中为用户名-VELU查找打开的游标
转到ORACLE计算机,并以sysdba身份启动sqlplus。
[oracle@db01 ~]$ sqlplus / as sysdba
然后跑
SELECT A.VALUE, S.USERNAME, S.SID, S.SERIAL# FROM V$SESSTAT A, V$STATNAME B, V$SESSION S WHERE A.STATISTIC# = B.STATISTIC# AND S.SID = A.SID AND B.NAME = 'opened cursors current' AND USERNAME = 'VELU';
ORA-01000(最大打开游标错误)是Oracle数据库开发中极为常见的错误。在Java上下文中,当应用程序尝试打开比数据库实例上配置的游标更多的ResultSet时,就会发生这种情况。
常见原因有:
配置错误
在应用程序中,查询数据库的线程比数据库中的游标的线程更多。一种情况是你的连接和线程池大于数据库上的游标数。
解:
增加数据库上的游标数量(如果资源允许)或
游标泄漏
应用程序未关闭ResultSet(在JDBC中)或游标(在数据库上的存储过程中)
背景
本节描述了游标背后的一些理论以及应如何使用JDBC。如果你不需要了解背景,可以跳过此步骤,直接进入“消除泄漏”。
什么是游标?
游标是数据库上的资源,用于保存查询的状态,特别是读取器在ResultSet中的位置。每个SELECT语句都有一个游标,并且PL / SQL存储过程可以打开并根据需要使用任意数量的游标。你可以在Orafaq上找到有关游标的更多信息。
数据库实例通常服务于几种不同的模式,许多不同的用户各自具有多个会话。为此,它具有可用于所有模式,用户和会话的固定数量的游标。当所有游标都处于打开状态(使用中)并且要求新游标的请求进入时,请求失败,并出现ORA-010000错误。
查找和设置光标数量
该号码通常由DBA在安装时配置。可以在Oracle SQL Developer的管理员功能中访问当前使用的游标数量,最大数量和配置。在SQL中,可以使用以下命令进行设置:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
将JVM中的JDBC与数据库上的游标相关联
下面的JDBC对象与以下数据库概念紧密相关:
JDBC是线程安全的:在线程之间传递各种JDBC对象是完全可以的。
例如,你可以在一个线程中创建连接。另一个线程可以使用此连接来创建PreparedStatement,第三个线程可以处理结果集。唯一的主要限制是,你随时都不能在一个PreparedStatement上打开多个ResultSet。请参见Oracle DB每个连接是否支持多个(并行)操作?
请注意,数据库提交发生在连接上,因此该连接上的所有DML(INSERT,UPDATE和DELETE)都将一起提交。因此,如果要同时支持多个事务,则每个并发事务必须至少具有一个Connection。
关闭JDBC对象
执行ResultSet的典型示例是:
Statement stmt = conn.createStatement(); try { ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" ); try { while ( rs.next() ) { System.out.println( "Name: " + rs.getString("FULL_NAME") ); } } finally { try { rs.close(); } catch (Exception ignore) { } } } finally { try { stmt.close(); } catch (Exception ignore) { } }
请注意,finally子句如何忽略close()引发的任何异常:
持有JDBC对象
JDBC对象可以安全地保存在局部变量,对象实例和类成员中。通常更好的做法是:
消除泄漏
有许多可用于帮助检测和消除JDBC泄漏的过程和工具:
开发实践:良好的开发实践应在软件离开开发人员之前减少软件中的错误数量。具体做法包括:
1. 配对编程,以教育没有足够经验的人 2. 代码审查,因为许多眼睛胜过一只眼睛 3. 单元测试,这意味着你可以使用测试工具来练习所有代码库,从而使重现泄漏变得微不足道 4. 使用现有的库进行连接池,而不是构建自己的库
静态代码分析:使用出色的Findbugs之类的工具来执行静态代码分析。这会拾取许多未正确处理close()的地方。Findbugs有一个用于Eclipse的插件,但也可以一次性运行,并已集成到Jenkins CI和其他构建工具中
在运行时:
可保持性和提交
如果ResultSet的可保存性为ResultSet.CLOSE_CURSORS_OVER_COMMIT,则在调用Connection.commit()方法时关闭ResultSet。可以使用Connection.setHoldability()或使用重载的Connection.createStatement()方法进行设置。 2. 在运行时记录。
你可以在项目中添加调试JDBC驱动程序(用于调试-请勿实际部署)。一个示例(我还没有使用过)是log4jdbc。然后,你需要对此文件进行一些简单的分析,以查看哪些执行没有相应的关闭。计算打开和关闭应该突出显示是否存在潜在问题
监视数据库。使用诸如SQL Developer的“ Monitor SQL”功能或Quest的TOAD之类的工具监视正在运行的应用程序。本文介绍了监视。在监视期间,你查询打开的游标(例如,从表v $ sesstat中)并查看其SQL。如果游标的数量在增加,并且(最重要的是)由一个相同的SQL语句控制,则你知道该SQL泄漏。搜索你的代码并查看。
其他想法
你可以使用WeakReferences处理关闭的连接吗? 弱引用和软引用是允许你以允许JVM在其认为合适的任何时间对对象进行垃圾收集的方式来引用对象的方法(假定该对象没有强大的引用链)。
如果将构造函数中的ReferenceQueue传递给软引用或弱引用,则当对象发生GC对象时(如果根本发生),该对象将被放置在ReferenceQueue中。使用这种方法,你可以与对象的终结处理进行交互,并且可以在此时关闭或终结该对象。
幻像引用有些古怪;它们的目的仅是控制最终确定,但是你永远无法获得对原始对象的引用,因此很难在其上调用close()方法。
但是,尝试控制何时运行GC并不是一个好主意(Weak,Soft和PhantomReferences 在对象已排队进入GC 之后让你知道)。实际上,如果JVM中的内存量很大(例如-Xmx2000m),则可能永远不会对对象进行GC,并且仍然会遇到ORA-01000。如果JVM内存相对于程序要求而言较小,则可能会发现ResultSet和PreparedStatement对象在创建后立即被GC(在你可以从它们读取之前),这很可能会使程序失败。
TL; DR:弱引用机制不是管理和关闭Statement和ResultSet对象的好方法。