Oracle ORA-04031 故障排查
Oracle ORA-04031 故障排查
一、问题现象
用户反馈数据库操作失败,应用日志中出现:
ORA-04031: unable to allocate 4160 bytes of shared memory
同时,DBA 发现 AWR 快照未生成,怀疑数据库内部出现严重内存问题。
二、排查流程总览
- 确认错误发生时间与频率
- 定位 Alert Log,提取 trace 文件路径
- 分析 trace 文件中的 HEAP DUMP
- 判断是否因碎片化或保留池策略导致
- 制定解决方案
三、详细排查步骤
步骤 1:查看 Alert Log —— 找到错误入口
查询日志路径 , 以 sysdba 身份登录数据库,执行:
-- 推荐方式(11g+)
SELECT value FROM v$diag_info WHERE name = 'Diag Trace';
输出示例:
/u01/app/oracle/diag/rdbms/orcl/orcl/trace
🔍 操作命令:
cd $ORACLE_BASE/diag/rdbms/orcl/orcl/trace
grep -A5 -B5 "ORA-04031" alert_orcl.log
📄 日志片段(模拟真实):
Tue Jan 14 05:03:22 2026
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_28471.trc:
ORA-04031: unable to allocate 4160 bytes of shared memory
("shared pool","unknown object","sga heap(6,0)","modification ")
Tue Jan 14 05:03:23 2026
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_28472.trc:
ORA-04031: unable to allocate 4160 bytes of shared memory
("shared pool","unknown object","sga heap(6,0)","modification ")
✅ 关键信息标注:
| 日志内容 | 含义 | 为什么重要 |
|---|---|---|
ORA-04031: unable to allocate 4160 bytes |
申请 4160 字节失败 | 确认是小块内存分配失败(<4400) |
"shared pool" |
错误发生在共享池 | 排除 PGA、Large Pool 等其他区域 |
"sga heap(6,0)" |
具体子池和 duration | 表明是 第6个子池的永久区(duration 0) 出问题 |
orcl_ora_28471.trc |
对应的 trace 文件 | 下一步分析入口 |
💡 结论:问题集中在 subpool 6 的 duration 0 区域,且请求大小为 4160 字节。
步骤 2:分析 Trace 文件 —— 深入 HEAP DUMP
🔍 操作命令:
less orcl_ora_28471.trc
# 在 less 中输入:
/HEAP DUMP
📄 日志片段(重点节选 + 注释):
*** 2026-01-14 05:03:22.123
HEAP DUMP heap name="sga heap(6,0)" addr=0x7f8b00000000
extent sz=0xfe0 alt=32767 het=32767 rec=0 flg=2 opc=2
EXTENT 0 addr=0x7f8b00000000
Chunk 7f8b00000010 sz= 200 free " "
Chunk 7f8b000000e0 sz= 10000 perm "perm "
Chunk 7f8b000027f0 sz= 160 free " "
Chunk 7f8b000028a0 sz= 8192 perm "perm "
...(省略大量 perm 块)...
=======================================
Total heap size = 805306104 <-- 总大小 ≈768 MB
FREE LISTS:
Free list length=12
Chunk 7f8b00000010 sz= 200 free
Chunk 7f8b000027f0 sz= 160 free
...(其他 free 块均 < 500 字节)...
Total free space = 360 <-- 总空闲仅 360 字节!
Largest free chunk = 200 <-- 最大连续空闲块仅 200 字节!
Reserved FREE LISTS:
Empty <-- 保留池未被使用(因为请求 <4400)
=======================================
✅ 逐行问题标注:
| 日志行 | 分析 | 体现的问题 |
|---|---|---|
heap name="sga heap(6,0)" |
当前分析的是第6子池的 duration 0 区 | 局部耗尽:其他子池可能有空闲,但无法借用 |
perm "perm" 占绝大多数 |
大量永久对象(如 pinned cursor) | duration 0 只增不减,长期积累撑爆该区域 |
Total free space = 360 |
总空闲极少 | 内存几乎耗尽 |
Largest free chunk = 200 |
最大连续空闲块仅 200 字节 | 碎片化严重,无法满足 4160 字节请求 |
Reserved FREE LISTS: Empty |
保留池未参与分配 | 因 **4160 🎯 核心结论: |
不是没内存,而是:
- 内存集中在
heap(6,0)且全是perm;- 空闲块太小(最大仅 200);
- 保留池因规则限制无法启用。
步骤 3:验证保留池策略(可选)
查看隐藏参数值:
SELECT x.ksppinm NAME, y.ksppstvl VALUE
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx
AND x.ksppinm = '_shared_pool_reserved_min_alloc';
输出:
NAME VALUE
_shared_pool_reserved_min_alloc 4400
✅ 证实:只有 ≥4400 字节的请求才能用保留池,4160 被排除在外。
四、根本原因总结
| 维度 | 说明 |
|---|---|
| 直接原因 | Shared Pool 子池 6 的 duration 0 区域碎片化,最大连续空闲块(200B) < 请求大小(4160B) |
| 深层机制 | Oracle 的 Duration 分区机制导致内存无法跨子池/跨 duration 共享 |
| 策略限制 | 保留池默认只服务 ≥4400B 的请求,小请求被拒之门外 |
| 触发场景 | 高频硬解析、大量 PL/SQL 编译、未使用绑定变量等导致 Shared Pool 压力剧增 |
五、解决方案(推荐顺序)
✅ 方案 1:关闭 Duration 机制(首选)
-- 1. 查看当前值(可选)
SELECT x.ksppinm NAME, y.ksppstvl VALUE
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx
AND x.ksppinm = '_enable_shared_pool_durations';
-- 2. 修改参数(需重启生效)
ALTER SYSTEM SET "_enable_shared_pool_durations" = FALSE SCOPE=SPFILE;
-- 3. 重启数据库
SHUTDOWN IMMEDIATE;
STARTUP;
📌 效果:所有子池内存统一管理,避免“一个池子爆满,其他池子空着”的问题。
Oracle 11g 中该参数默认为
TRUE关闭属于常见优化手段,被广泛应用于生产环境
| 方面 | 影响 |
|---|---|
| 正面 | 减少 ORA-04031 风险,内存利用率更均衡 |
| 潜在风险 | 极高并发下 library cache latch 争用可能略增(实际影响极小) |
| 兼容性 | 安全,Oracle 支持,无需补丁 |
⚠️ 方案 2:扩容 Shared Pool(辅助)
ALTER SYSTEM SET SHARED_POOL_SIZE = 2G SCOPE=BOTH;
❗ 注意:若不解决碎片问题,扩容后仍可能再次报错。
❌ 不推荐:调小 _shared_pool_reserved_min_alloc
-- 危险!可能导致大对象无法分配
ALTER SYSTEM SET "_shared_pool_reserved_min_alloc" = 4000;
🚫 后果:大量小请求涌入保留池,真正的大请求(如 50KB)反而失败。
六、预防建议
- 应用层:强制使用绑定变量,减少硬解析
- 监控:定期检查
v$sgastat中free memory - 参数固化:新环境部署时即关闭
_enable_shared_pool_durations - AWR 基线:建立正常时期的 Shared Pool 使用基线,便于异常对比
日常监控 SQL
-- 查看 Shared Pool 空闲情况
SELECT pool, name, bytes
FROM v$sgastat
WHERE pool = 'shared pool' AND name = 'free memory';
-- 查看子池分布(需有诊断包)
SELECT * FROM v$shared_pool_reserved;
七、附录:关键日志速查表
| 日志特征 | 问题指向 |
|---|---|
sga heap(X,0) |
Duration 0 区域耗尽(最常见) |
Largest free chunk < 4000 |
碎片化导致小请求失败 |
Reserved FREE LISTS: Empty |
保留池未启用(通常因 <4400) |
大量 perm 块 |
永久对象堆积,需检查 pinned cursor |
| 参数 | 默认值 | 作用 | 是否可调 |
|---|---|---|---|
_enable_shared_pool_durations |
TRUE | 是否启用 Duration 分区 | ✅ 可关 |
_shared_pool_reserved_min_alloc |
4400 | 使用保留池的最小申请大小(字节) | ❌ 不建议改 |
SHARED_POOL_SIZE |
自动/手动 | Shared Pool 总大小 | ✅ 可调 |
SHARED_POOL_RESERVED_SIZE |
≈10% of shared_pool | 保留池大小 | ⚠️ 一般不动 |
总结流程图
graph TD
A[应用报 ORA-04031] --> B{检查 Alert Log}
B --> C[定位 trace 文件]
C --> D[分析 HEAP DUMP]
D --> E{Largest Free Chunk < 请求大小?}
E -- 是 --> F[检查是否因 <4400 无法用保留池]
F --> G[确认 Duration 0 区域是否膨胀]
G --> H[关闭 _enable_shared_pool_durations]
H --> I[重启数据库]
I --> J[验证问题是否解决]
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 龙羽

