ORA-04031主因是Shared Pool碎片化而非内存不足;需通过X$KSMSP查最小/最大空闲块、recreate块占比及子池分布,并结合v$latch命中率和request_misses波动综合诊断。

ORA-04031频繁出现,但v$sgastat里free memory还剩几十MB?这不是内存不够,是Shared Pool碎片化在捣鬼。诊断必须绕过“总空闲量”这种误导性指标,直击内存块分布和分配行为本身。
看v$sgastat里的free memory为什么不准
共享池的free memory是所有空闲chunk大小的总和,不反映最大连续块。哪怕总空闲有50MB,如果全是2KB、4KB的小块,而当前请求要4KB以上(比如加载一个PL/SQL包),照样报ORA-04031。
-
v$sgastat中free memory持续低于5MB,是碎片严重的强信号;高于10MB也不能排除碎片——要看这些空间是否“能用” - 真正关键的是
v$sgastat里shared pool free memory行的bytes值,不是buffer cache或其他pool的 - 不要只查一次:用
SELECT * FROM v$sgastat WHERE name = 'free memory' AND pool = 'shared pool';每分钟跑一次,观察是否剧烈波动(碎片化时释放不及时,空闲量忽高忽低)
查v$shared_pool_advice判断是否真缺空间
这个视图模拟不同shared_pool_size下的parse time和misses,能区分“真小”和“假小”。如果建议值比当前大很多,且estimated_parse_time_saved为负,说明扩容可能加剧碎片而非缓解问题。
- 执行
SELECT shared_pool_size_for_estimate, estd_lc_memory_objects, estd_lc_time_saved FROM v$shared_pool_advice ORDER BY shared_pool_size_for_estimate; - 重点看
estd_lc_time_saved列:正值表示扩容能省时间,负值说明当前size已够,瓶颈在碎片或争用 - 若
estd_lc_memory_objects随size增大增长缓慢,说明对象缓存效率低,大概率是硬解析泛滥导致
用X$KSMSP定位碎片源头
X$KSMSP是Shared Pool内存块的原始快照,每一行是一个chunk。它不经过视图抽象,能直接看到块大小、类型和碎片分布。
- 查最小可用块:
SELECT MIN(ksmchsiz) FROM x$ksmsp WHERE ksmchcls = 'free';—— 如果长期卡在几百字节,说明碎片极细 - 查最大空闲块:
SELECT MAX(ksmchsiz) FROM x$ksmsp WHERE ksmchcls = 'free';—— 若远小于报错中请求的字节数(如报错unable to allocate 4160 bytes,而这里返回2048),就是碎片铁证 - 查
recreate类块占比:SELECT COUNT(*) FROM x$ksmsp WHERE ksmchcls = 'recreate';—— 过高(>70%)说明大量可重建对象被反复挤出/重载,加剧碎片
盯住latch: shared pool等待和request_misses
碎片化必然拉长free list搜索链,进而抬高latch争用。这不是间接证据,是内存分配路径受阻的实时体现。
- 查latch命中率:
SELECT name, gets, misses, ROUND(100*(1-misses/NULLIF(gets,0)),2) "Hit%" FROM v$latch WHERE name = 'shared pool';——Hit%低于99.5%即异常 - 查
v$sgastat中的free memory和request_misses两行:request_misses升高+free memory未同步下降=典型碎片场景 - 结合
v$session_wait过滤event = 'latch: shared pool',确认是否集中在特定应用模块或SQL模式
最易被忽略的一点:碎片问题往往藏在子池(subpool)内部。报错信息里的sga heap(6,0)指向第6个子池的duration 0区域,但v$sgastat和v$shared_pool_advice都是全局视图。必须用X$KSMSP配合ksmchidx字段(需SELECT ksmchidx, ksmchsiz, ksmchcls FROM x$ksmsp WHERE ksmchcls = 'free';)才能定位到具体哪个子池的哪段duration出了问题。


















