必须用DBMS_LOB.FREETEMPORARY而非删表或TRUNCATE,因为临时LOB不属用户表、由Oracle内部管理在TEMP表空间,仅能通过该过程显式释放;未配对调用会导致PGA内存泄漏和ORA-01652错误。
为什么清理临时LOB对象必须用 DBMS_LOB.FREETEMPORARY 而不是直接删表或 truncate?
oracle 中由存储过程(尤其是使用 dbms_lob.createtemporary 创建的)生成的临时 lob,不会随会话结束自动释放——尤其在异常退出、pl/sql 异常未捕获、或使用了自治事务时,这些 lob 会滞留在 temp 表空间,持续占用空间并可能触发 ora-01652: unable to extend temp segment 错误。
直接 DROP TABLE 或 TRUNCATE 对它们无效,因为临时 LOB 不属于任何用户表,而是由 Oracle 内部管理的内存/临时段。必须显式调用 DBMS_LOB.FREETEMPORARY 才能释放。
- 仅对当前会话中通过
DBMS_LOB.CREATETEMPORARY创建的 LOB locator 有效;传入 NULL 或已释放的 locator 会报ORA-22275: invalid LOB locator specified - 不能跨会话释放其他会话创建的临时 LOB
- 若 LOB 已被
DBMS_LOB.FREETEMPORARY释放过,再次调用会报错,需加异常捕获
如何在存储过程中安全释放临时 CLOB/BLOB 变量?
关键不是“事后批量清理”,而是在过程逻辑出口处主动释放——否则脚本跑完后 LOB 还挂着。常见错误是只在正常路径释放,忽略异常分支。
推荐写法:用 EXCEPTION 块 + WHEN OTHERS THEN 统一兜底,并在 FINALLY(Oracle 12c+ 支持)或双层异常处理中确保释放。
DECLARE
l_clob CLOB;
BEGIN
DBMS_LOB.CREATETEMPORARY(l_clob, TRUE);
-- ... 主逻辑,可能抛异常
DBMS_LOB.WRITEAPPEND(l_clob, 10, 'hello world');
EXCEPTION
WHEN OTHERS THEN
IF DBMS_LOB.ISTEMPORARY(l_clob) = 1 THEN
DBMS_LOB.FREETEMPORARY(l_clob);
END IF;
RAISE;
END;
-
DBMS_LOB.ISTEMPORARY必须先检查,避免对空或非临时 LOB 调用FREETEMPORARY - 即使
l_clob是局部变量,只要没显式释放,会话级 LOB 段不会自动回收 - 不要依赖变量作用域自动清理——Oracle 不做这件事
如何查出当前会话残留的未释放临时 LOB?
没有直接视图列出“未释放的临时 LOB”,但可通过 V$TEMPSEG_USAGE 和 V$SESSION 关联定位可疑会话:
SELECT s.sid, s.serial#, s.username, t.sql_id, t.segtype, t.blocks * 8 / 1024 AS mb FROM v$tempseg_usage t JOIN v$session s ON t.session_addr = s.saddr WHERE t.segtype = 'LOB' ORDER BY t.blocks DESC;
- 注意
segtype = 'LOB'行代表该会话正在使用临时 LOB 段(含临时 LOB 和排序/哈希等其他临时段) - 结合
sql_id查对应 SQL,看是否来自未正确释放的存储过程 - 无法直接知道哪个 PL/SQL 变量没释放,只能靠代码审计 + 上述监控辅助定位
能否用 job 定期清理其他会话的临时 LOB?
不能。Oracle 不允许一个会话调用 DBMS_LOB.FREETEMPORARY 释放另一个会话创建的临时 LOB。所谓“定期清理脚本”本质是无效的——你只能清理自己会话里的。
真正可行的运维动作只有两个:
- 重启数据库实例(极端手段,会清空所有会话级临时段,但影响大)
- 杀掉长期持有大量临时 LOB 的会话:
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;,之后其临时段会被自动回收
所以重点永远在开发侧:每个 CREATETEMPORARY 必须配对 FREETEMPORARY,且覆盖所有执行路径。临时 LOB 的生命周期管理,本质上是个编码契约,不是 DBA 能后期补救的事。


















