服务公告

服务公告 > 常见问题 > 内存明明够用却报 OOM:overcommit 与地址空间残留

内存明明够用却报 OOM:overcommit 与地址空间残留

发布时间:2026-10-16 16:41
## 现象是什么 free 显示可用内存还有不少,进程却报出内存不足:应用日志里 cannot allocate memory,或者程序直接启动失败,系统层面 OOM 事件也没发生过。这违反直觉的场景来自 Linux 内存管理的两个机制:内存申请的宽许策略与虚拟地址空间的限制。申请不等于占用,报 OOM 也不一定真的没内存。 ## 原理一:申请不等于占用 程序向内核申请内存时,内核默认采用宽许策略:先答应下来,实际写入时才真正分配物理页。于是进程可以申请远超物理内存的总额度,绝大多数程序申请后只用一部分。宽许策略的价值是让稀疏分配的程序高效运行,代价是当所有进程真把申请的内存写满时,物理内存瞬间击穿。反过来排查时要注意:free 的可用数只反映物理层,看不到已被"承诺"出去的额度。极端调整是把策略改成严格模式,按真实可用量拒绝申请,但会误伤稀疏分配的程序,通常不建议动。 ## 原理二:地址空间的上限 第二个机制更隐蔽:每个进程有虚拟地址空间上限,64 位系统上一般触不到,32 位进程却只有三四 G 可用。Java 这类自己管理内存的程序,堆加堆外加直接内存的虚拟地址映射总量逼近上限时,物理内存再富裕也一样报分配失败——报错位置常在堆外分配,容易被误诊为内存泄漏。glibc 的 malloc 默认给每个线程分配独立的分配区,线程多的程序虚拟地址消耗成倍放大,也是"看着没占多少却报不足"的常客。 ## 排查方法 对着报错进程查两本账: ```bash grep -E 'VmSize|VmRSS' /proc/进程号/status ``` VmSize 是虚拟地址空间总量,VmRSS 是实际物理占用。RSS 远小于 VmSize 且报错,指向地址空间问题;RSS 逼近物理内存,就是真不足。再对照宽许额度的统计文件 /proc/meminfo 里的 Commit 一行,看全系统承诺是否已到上限。 **处理建议。**地址空间类问题:32 位程序迁移到 64 位是根治;线程过多的程序限制线程数或调整分配区参数;Java 调小堆外直接内存上限。物理内存类问题:老实扩容或查泄漏。顺云云服务器遇到这类玄学报错,先用两本账定位类型,再对症下药,比盲目加内存有效。