PL/SQL集合变量不显式释放会持续占用PGA内存直至会话结束;DELETE仅逻辑清空、不释放内存,TRIM或赋值NULL才真正释放;批量处理应分块LIMIT并及时TRIM。
PL/SQL里集合变量不释放会吃光PGA
pl/sql中用 varchar2、number 等类型声明的集合(如 table of 或 varray),只要没显式清空或超出作用域,其内存就一直挂在当前会话的 pga 里。这不是“自动回收”的场景——哪怕你只循环插入 10 万条记录进一个 my_tab sys.odcivarchar2list,不处理,它就占着内存不放,直到会话结束。
常见错误现象:ORA-04030: out of process memory 或执行变慢、后续 PL/SQL 块报 ORA-06502: PL/SQL: numeric or value error(实际是内存不足触发的间接报错)。
- 使用场景:批量数据预处理、ETL 中缓存中间结果、分页组装大对象后统一提交
-
COLLECT INTO、BULK COLLECT加载数据后务必配套清理 -
DELETE方法本身不释放内存,只是把元素逻辑置空;真正释放靠TRIM或重新赋值:= NULL
DELETE 和 TRIM 的区别不是“删不删”,而是“还占不占内存”
DELETE 是集合方法,调用后集合长度变为 0(COUNT = 0),但底层分配的内存块仍保留;TRIM(n) 才真正收缩内存,把末尾 n 个元素连同其占用空间一并丢弃。如果想彻底清空,TRIM(COUNT) 或直接 my_coll := my_coll%TYPE() 更可靠。
示例对比:
DECLARE l_list SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST(1,2,3,4,5); BEGIN l_list.DELETE; -- COUNT=0,但 PGA 还占着约 5 个 NUMBER 的空间 DBMS_OUTPUT.PUT_LINE(l_list.COUNT); -- 输出 0 <p>l_list.TRIM(5); -- 彻底释放全部内存 -- 或写成:l_list := SYS.ODCINUMBERLIST(); END;
-
DELETE后还能调用EXTEND继续追加,说明内存没还 -
TRIM后再EXTEND会重新分配,有轻微开销但更安全 - 对
VARRAY,TRIM受限于最大尺寸定义,DELETE不支持(编译报错)
监控当前会话 PGA 使用量不能只看 v$sesstat
v$sesstat 里 session pga memory 和 session pga memory max 是累计峰值,不反映实时集合变量占用。真要看某次执行中集合“正在吃多少”,得结合 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—— 它只对某些 Oracle 内部缓存有效,对 PL/SQL 集合无效 - Oracle 12c+ 可开启
ALTER SESSION SET EVENTS '10046 trace name context forever, level 8',在 trace 文件里搜pga和heap看分配细节
批量处理时别用单一大集合,改用分块 + 显式释放
一次性 BULK COLLECT INTO 百万行进集合,等于一次性向 PGA 申请巨量连续内存,极易触发 ORA-04030。更稳的做法是限定 LIMIT,每次只取几千行,处理完立刻 TRIM 或重置变量。
示例节选:
DECLARE
TYPE t_rows IS TABLE OF emp%ROWTYPE;
l_batch t_rows;
CURSOR c_emp IS SELECT * FROM emp;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp BULK COLLECT INTO l_batch LIMIT 1000;
EXIT WHEN l_batch.COUNT = 0;
<pre class='brush:php;toolbar:false;'>-- 处理 l_batch...
l_batch.TRIM(l_batch.COUNT); -- 关键:立刻释放本次批次内存
-- 或:l_batch := t_rows(); END LOOP; CLOSE c_emp; END;
-
LIMIT值不是越大越好,500–5000 是较稳妥区间,取决于单行平均大小 - 不要在循环外声明集合再反复
EXTEND累积——这等价于不做分块 - 若用
PIPELINED函数返回集合,注意调用方也要及时消费,否则管道缓冲区也会占 PGA
PGA 的实际占用比文档写的更“粘滞”,尤其在长事务或复杂嵌套块里。集合变量一旦被引用过,它的内存块生命周期就和会话强绑定,靠“等它自己消失”基本不可行。










