Shared Pool Free Memory高但ORA-04031频发,本质是内存碎片化导致duration 0分配失败;需通过v$sgastat查小字节free块、V$SQL_SHARED_MEMORY定位异常堆,并禁用ASMM干预以缓解。

Shared Pool Free Memory高但ORA-04031频发,先看v$sgastat的chunk分布
AWR报告里Shared Pool Free %显示78%,你以为内存很宽裕?错。真正致命的是内存被切成大量小碎块,无法满足新SQL解析所需的连续空间。ORA-04031报错中"sga heap(6,0)"这类提示,往往就是duration 0(调用期)内存分配失败的直接证据。
验证碎片必须绕过AWR,直查底层:
- 运行
SELECT * FROM v$sgastat WHERE name = 'free memory' AND pool = 'shared pool',确认free总量 - 再跑
SELECT bytes, count(*) FROM v$sgastat WHERE pool = 'shared pool' GROUP BY bytes ORDER BY bytes DESC - 若结果中大量出现
4096、8192、16384字节的free记录,就是典型碎片——这些碎块连一条简单SELECT的parse heap都填不满
AWR中“Shared Pool Statistics”里的两个百分比是碎片间接指标
% SQL with executions>1和% Memory for SQL w/exec>1这两个值如果持续偏低(比如% SQL with executions>1
- 当
% SQL with executions>1远低于% Memory for SQL w/exec>1时,说明重用SQL占内存少,但单次SQL却占了大头——典型的“内存大户+低复用”组合 - 结合
Top 5 Timed Events中是否出现library cache lock或row cache lock,可交叉判断是否因碎片引发解析链阻塞 - 注意:该指标来自
DBA_HIST_SQL_SUMMARY,反映的是历史快照间的变化,不能替代实时v$sgastat诊断
别信“Shared Pool自动增长”,收缩/增长本身就会加剧碎片
在ASMM模式下,shared_pool_size看似动态调整,但每次shrink都会强制淘汰部分缓存对象(包括dc_*条目),而grow又需要拼凑连续内存。这两者都在制造或放大碎片。
- AWR报告“Memory Parameters”页中若发现
shared pool的Begin/End值波动剧烈,尤其伴随Row Cache Hit %同步下跌,基本可锁定是ASMM干预引发的连锁反应 - 19c中默认启用
ENABLE_DDL_LOGGING=TRUE,若日志表未分区,每次DDL都会刷新dc_logstdby等缓存,触发大量duration 0内存申请,进一步撕裂空闲链表 - 临时解法:设
shared_pool_size为固定值(即禁用ASMM对该组件的控制),避免无谓伸缩
V$SQL_SHARED_MEMORY能定位到具体SQL的堆级内存问题
当碎片已成事实,光看总量没用。V$SQL_SHARED_MEMORY视图暴露每个子游标(child_number)的各内存堆(heap)大小与状态,是唯一能定位“谁在吃碎块”的工具。
- 查某条高频SQL:
SELECT heap#, bytes, status FROM v$sql_shared_memory WHERE kglhdadr IN (SELECT address FROM v$sqlarea WHERE sql_text LIKE 'SELECT%FROM orders%') - 重点关注
heap# = 6(执行计划堆)和heap# = 0(SQL文本堆)是否异常庞大——复杂执行计划或超长SQL文本会直接撑爆duration 0内存 - 若同一SQL多个
child_number对应大量小bytes堆(如一堆2048字节),说明该SQL在不同环境反复硬解析,且每次分配都卡在碎块上
碎片不是“调大shared_pool_size”就能解决的问题;它藏在duration 0内存分配逻辑里,藏在ASMM的自动伸缩行为里,更藏在应用层SQL写法与DDL频率的细节里。AWR只提供线索,真要动手,得切到v$sgastat和V$SQL_SHARED_MEMORY里一帧帧对。











