pl/sql集合变量不显式清理会持续占用pga内存直至会话结束;delete仅逻辑清空,trim或赋值null才释放内存;批量处理须分块limit+及时trim,监控需查v$process.pga_used_mem。

PL/SQL集合变量不释放会持续吃光PGA
PL/SQL里声明的集合(如SYS.ODCIVARCHAR2LIST、TABLE OF NUMBER)只要没显式清理,其内存就一直挂在当前会话的PGA里,直到会话结束。这不是GC场景——哪怕你只BULK COLLECT了10万行,后续不做处理,这块内存就锁死不动。
常见错误现象:ORA-04030: out of process memory,或间接触发ORA-06502(实际是内存耗尽导致的类型校验失败)。
-
DELETE只是逻辑清空:调用后COUNT = 0,但底层内存块仍保留,EXTEND还能继续追加 -
TRIM(n)才真正收缩内存:丢弃末尾n个元素及其占用空间;想彻底清空,用TRIM(COUNT)或直接赋值:= NULL -
VARRAY不支持DELETE(编译报错),只能用TRIM或重置
批量处理必须分块+及时TRIM
一次性加载全量数据进集合再处理,是PGA爆掉的最常见原因。尤其在ETL中间缓存、分页组装大对象、COLLECT INTO等场景下,风险极高。
实操建议:
- 用
BULK COLLECT ... LIMIT n控制每次加载行数,n建议设为1000–5000(视单行大小调整) - 每轮处理完立即调用
my_coll.TRIM(my_coll.COUNT),或更稳妥地重置my_coll := my_coll%TYPE() - 避免在循环内反复
EXTEND累积——改用预分配+索引赋值,减少内存重分配开销
监控不能只看v$sesstat
v$sesstat里的session pga memory和session pga memory max是累计峰值,无法反映某次PL/SQL执行中集合变量“此刻正在占多少”。真要看实时占用,得关联v$process和v$session查pga_used_mem变化趋势。
执行前/后快照对比命令:
SELECT pga_used_mem FROM v$process p JOIN v$session s ON p.addr = s.paddr WHERE s.sid = SYS_CONTEXT('USERENV','SID');
注意:DBMS_SESSION.FREE_UNUSED_USER_MEMORY对PL/SQL集合变量无效,它只清理Oracle内部缓存碎片。
SGA/PGA配置影响PL/SQL执行上限
即使代码写得再干净,底层内存策略不合理也会让PL/SQL频繁OOM。自动内存管理(AMM)在小内存环境(≤4GB)下可用,但生产环境普遍用自动共享内存管理(ASMM):固定sga_target和pga_aggregate_target,再由Oracle动态调度。
- 如果发现PL/SQL常因
ORA-04030失败,先检查pga_aggregate_target是否过小(比如仅256MB却跑大批量集合操作) - 不要盲目调大
sga_max_size来“解决”内存问题——SGA和PGA争内存反而加剧抖动 - 用
show parameter pga和show parameter sga确认当前配置,结合v$pgastat看实际使用率
真正容易被忽略的是:集合变量的生命周期和会话绑定太紧,而开发时往往只关注逻辑正确性,忘了它在PGA里“赖着不走”。一个TRIM或:= NULL动作,可能就是避免整体会话重启的关键操作。











