显式游标必须手动open/close,漏close会导致ora-01000和pga内存持续上涨;bulk collect须配limit防oom;for loop游标虽自动关闭,但仅适用于隐式声明场景,sys_refcursor不支持该语法。

显式游标必须手动配对OPEN/CLOSE,且异常分支里也要关
漏掉CLOSE是ORA-01000和PGA内存持续上涨的最直接原因。PL/SQL声明游标(CURSOR c IS SELECT ...)只是定义,不占用资源;OPEN c才真正分配服务器端游标句柄和PGA内存;而没CLOSE,这些资源就不会释放——哪怕块执行结束,只要发生异常跳过正常路径,游标就滞留在会话中。
常见错误包括:把CLOSE只写在正常流程末尾、在循环内重复OPEN同一游标、或用FOR c IN (SELECT ...)误以为“自动关”就不管显式游标状态。
- 所有
OPEN后必须跟CLOSE,且要出现在EXCEPTION块里 - 用
IF c%ISOPEN THEN CLOSE c;判断再关,避免对未打开或已关闭游标调用CLOSE引发ORA-01001 - 不要把游标声明在包级变量里长期持有,尤其不能跨调用复用同一游标变量
BULK COLLECT不加LIMIT等于把整张表拖进内存
BULK COLLECT INTO本身不危险,危险的是没配LIMIT。它会一次性把结果集全部加载进PL/SQL集合(如TABLE OF),若查询返回百万行,PGA立刻OOM,甚至触发PLS-00302或静默失败。
正确做法是控制单次提取量,并及时清空集合:
- 用
FETCH c BULK COLLECT INTO t_array LIMIT 1000,数值按单行平均大小和可用PGA估算(通常100–5000较安全) -
BULK COLLECT后立即处理数据,然后调用t_array.DELETE释放集合引用 - 别对超大结果集用
FOR rec IN (SELECT ...)——它虽不缓存全量,但游标保持打开时间过长,且无法中断重跑
FOR循环游标自动关,但只适用于隐式声明场景
FOR r IN (SELECT ...)或FOR r IN c LOOP(其中c是CURSOR c IS ...声明的显式游标)确实会在循环结束时自动CLOSE。但这不等于“不用管”,因为底层仍消耗一个游标槽位,且有隐性风险:
- 循环体里做耗时操作(如远程调用、写文件),游标保持打开状态,延长服务器端资源占用
- 无法控制fetch size,Oracle版本升级后可能预取更多行(如19c+默认更激进)
- 不能中途退出后继续(不像显式游标可记录
%ROWCOUNT),重跑就得全量重查 - 若游标变量是
SYS_REFCURSOR类型,FOR r IN c LOOP直接编译报错:PLS-00221: 'c' is not a cursor
别靠调大open_cursors掩盖泄露
open_cursors是每个会话的软限制,默认300。ORA-01000出现,说明当前会话打开了太多游标没关——不是数据库不够用,而是代码在泄漏。盲目调大参数(如设到2000)只能延缓报错,但PGA内存仍在涨,最终可能OOM或拖慢整个实例。
真正该做的:
- 查真实泄露点:
SELECT sql_id, sql_text FROM v$open_cursor WHERE sid = SYS_CONTEXT('USERENV', 'SID') ORDER BY last_sql_time DESC - 监控PGA使用:
SELECT name, value/1024/1024 "MB" FROM v$pgastat WHERE name IN ('total PGA allocated', 'total PGA used mem') - JDBC应用重点查
PreparedStatement是否在finally或try-with-resources里关闭,连接池归还前是否清理语句
游标生命周期管理的关键不在“怎么关”,而在“什么时候必须自己开、自己关、自己控节奏”——自动关的语法糖只覆盖有限场景,其余都得亲手捏住OPEN和CLOSE的时机。











