包级变量生命周期绑定会话,长连接中持续驻留PGA导致内存泄漏;短连接因会话频繁重建而无此问题;需通过DBMS_SESSION.RESET_PACKAGE显式重置或强制杀会话释放。

包级变量在长连接中持续驻留的机制
Oracle PL/SQL 包的全局变量(即包体或包规范中声明的 g_counter、g_cache_tab 这类变量)生命周期绑定到会话(session),不是每次调用函数就重建,而是在会话首次调用该包任意过程/函数时初始化一次,之后全程保留在 PGA 中。只要会话不结束,这些变量及其引用的数据(比如 PL/SQL TABLE 存了上万行记录)就一直占着内存——哪怕后续所有业务逻辑都再没访问过它。
为什么短连接看不出来,长连接立刻爆内存
短连接场景下,应用每次请求建新会话、执行完立刻断开,包变量随会话销毁一并释放;但 HIS、后台服务、PB9 客户端这类长连接,一个会话可能运行数天甚至数月。若包里用了大集合缓存、递归累积计数器、或未清空的游标结果集,内存只会单向增长。
-
DBMS_SESSION.RESET_PACKAGE可重置当前会话的包状态,但必须显式调用,且不能自动触发 - 包变量若引用了
REF CURSOR或BLOB,还可能间接持有临时段或 LOB locator,进一步拖慢释放 - 即使包里写了
g_cache_tab.DELETE,若没真正执行(比如异常跳过、条件未满足),变量仍维持原状
排查时容易误判的两个现象
直接查 v$process.pga_used_mem 或 v$sesstat 看不到“是哪个包占的”,因为 Oracle 不暴露包变量的内存归属。你只能通过排除法定位:
- 确认会话长时间未断开(
last_call_et > 3600且status = 'INACTIVE') - 对比相同业务逻辑在短连接(如 SQL*Plus 临时会话)下内存是否稳定
- 在怀疑包中插入
DBMS_OUTPUT.PUT_LINE('mem: ' || v$process.pga_used_mem)日志(需提前开启SET SERVEROUTPUT ON) - 用
ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'抓 trace,观察包内循环/赋值是否持续发生
真正可控的缓解手段
不能指望 Oracle 自动回收包变量,必须从设计和运维双线控制:
- 禁止在包中声明大容量集合类型变量,改用临时表或带
PRAGMA SERIALLY_REUSABLE的包(注意:该 pragma 不适用于含持久状态的包) - 所有长连接服务启动时,用
DBMS_SESSION.SET_IDENTIFIER打标,便于后续按标识批量清理(配合DBA_SESSIONS过滤) - 对 PB9/HIS 类系统,必须为后台服务单独建 Profile,禁用
IDLE_TIME,但启用CONNECT_TIME做最大生命周期限制(比如设为 24 小时强制重连) - 若已出现内存堆积,
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE是唯一有效释放方式——别信“等它自己清”,SMON 不管包变量
包变量的内存不释放不是 bug,是 Oracle 会话模型的固有行为。问题不在语法,而在长连接场景下没人主动重置它。


















