首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >主机内存有空间,但为什么还会报out of memory?

主机内存有空间,但为什么还会报out of memory?

作者头像
bisal
发布2025-11-12 17:38:04
发布2025-11-12 17:38:04
3650
举报

技术社群的这篇文章《MemFree 辣么大,为啥报 out of memory?》给我们讲解了Linux操作系统中内存出现out of memory的一种场景,可能日常会碰到系统内存用度的计算,可以学习了解。

背景

主机上 MemFree 还剩 2GB 的时候,登陆容器居然报 out of memory ...

报错操作

登录 OAT 容器报错。

代码语言:javascript
复制
docker exec -it oat /bin/bash

报错内容

代码语言:javascript
复制
OCI runtime state failed: runc did not terminate sucessfully: fatal error: runtime: out of memory

查看容器状态

代码语言:javascript
复制
# docker ps -f name=oat
CONTAINER ID        IMAGE                                                             COMMAND                  CREATED             STATUS              PORTS               NAMES
cf4cb4a2c359        reg.docker.alibaba-inc.com/oceanbase/oat:4.3.2_bp1_20250711_x86   "/oat/distribution/p…"   7 hours ago         Up 7 hours                              oat

容器状态:从容器状态看,容器是正常运行。OAT 也能正常登录、操作。

查看容器的资源限制

代码语言:javascript
复制
# docker stats oat --no-stream
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
cf4cb4a2c359        oat                 0.21%               483.3MiB / 3.701GiB   12.75%              0B / 0B             784MB / 95.3MB      97

# docker inspect oat | grep -i "memory"
            "Memory": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,

容器限制:未对容器做内存资源的限制,且容器使用的内存资源也并不高。

查看 messages 信息,确认是否存在 OOM 日志

代码语言:javascript
复制
# grep -E -i 'OOM|out of memory' /var/log/messages
Sep 23 01:31:58 localhost dockerd: time="2025-09-23T09:31:58.873152057+08:00" level=error msg="Error running exec cee8b083af29bef970aa60ff78c803e4ae8a6a4a14978aa62ae02a462f69b586 in container: OCI runtime state failed: runc did not terminate sucessfully: fatal error: runtime: out of memory\n\nruntime stack:\nruntime.throw(0xa1aa24, 0x16)\n\truntime/panic.go:1117 +0x72 fp=0x7ffd6d1993d0 sp=0x7ffd6d1993a0 pc=0x437bb2\nruntime.sysMap(0xc000000000, 0x4000000, 0xdcda50)\n\truntime/mem_linux.go:169 +0xc6 fp=0x7ffd6d199410 sp=0x7ffd6d1993d0 pc=0x41b0a6\nruntime.(*mheap).sysAlloc(0xdaf120, 0x400000, 0x0, 0x0)\n\truntime/malloc.go:729 +0x1e5 fp=0x7ffd6d1994b8 sp=0x7ffd6d199410 pc=0x40e545\nruntime.(*mheap).grow(0xdaf120, 0x1, 0x0)\n\truntime/mheap.go:1346 +0x85 fp=0x7ffd6d199520 sp=0x7ffd6d1994b8 pc=0x42a245\nruntime.(*mheap).allocSpan(0xdaf120, 0x1, 0x2c00, 0x0)\n\truntime/mheap.go:1173 +0x609 fp=0x7ffd6d1995a0 sp=0x7ffd6d199520 pc=0x42a049\nruntime.(*mheap).alloc.func1()\n\truntime/mheap.go:910 +0x59 fp=0x7ffd6d1995f0 sp=0x7ffd6d1995a0 pc=0x464ef9\nruntime.(*mheap).alloc(0xdaf120, 0x1, 0x220000012c, 0xffffffff)\n\truntime/mheap.go:904 +0x85 fp=0x7ffd6d199640 sp=0x7ffd6d1995f0 pc=0x4295e5\nruntime.(*mcentral).grow(0xdc1458, 0x0)\n\truntime/mcentral.go:232 +0x79 fp=0x7ffd6d199688 sp=0x7ffd6d199640 pc=0x41a699\nruntime.(*mcentral).cacheSpan(0xdc1458, 0x7ffd6d199738)\n\truntime/mcentral.go:158 +0x2ff fp=0x7ffd6d1996e0 sp=0x7ffd6d199688 pc=0x41a47f\nruntime.(*mcache).refill(0x7fd3b4b3d108, 0x2c)\n\truntime/mcache.go:162 +0xaa fp=0x7ffd6d199728 sp=0x7ffd6d1996e0 pc=0x41998a\nruntime.(*mcache).nextFree(0x7fd3b4b3d108, 0x7ffd6d19972c, 0x463ac5, 0x7ffd6d1997c8, 0x41005e)\n\truntime/malloc.go:882 +0x8d fp=0x7ffd6d199760 sp=0x7ffd6d199728 pc=0x40edad\nruntime.mallocgc(0x178, 0xa09b40, 0x7ffd6d199801, 0x7ffd6d199848)\n\truntime/malloc.go:1069 +0x850 fp=0x7ffd6d1997e8 sp=0x7ffd6d199760 pc=0x40f7b0\nruntime.newobject(0xa09b40, 0x463a80)\n\truntime/malloc.go:1177 +0x38 fp=0x7ffd6d199818 sp=0x7ffd6d1997e8 pc=0x40fa18\nruntime.malg(0x8000, 0x0)\n\truntime/proc.go:3988 +0x31 fp=0x7ffd6d199858 sp=0x7ffd6d199818 pc=0x442db1\nruntime.mpreinit(0xd97ba0)\n\truntime/os_linux.go:355 +0x29 fp=0x7ffd6d199878 sp=0x7ffd6d199858 pc=0x4347c9\nruntime.mcommoninit(0xd97ba0, 0xffffffffffffffff)\n\truntime/proc.go:744 +0xf7 fp=0x7ffd6d1998c0 sp=0x7ffd6d199878 pc=0x43ba77\nruntime.schedinit()\n\truntime/proc.go:637 +0xaf fp=0x7ffd6d199920 sp=0x7ffd6d1998c0 pc=0x43b5ef\nruntime.rt0_go(0x7ffd6d199b88, 0x9, 0x7ffd6d199b88, 0x4005d8, 0x881252, 0x900000000, 0x7ffd6d199b88, 0x46c3c0, 0x10000, 0x10000, ...)\n\truntime/asm_amd64.s:220 +0x125 fp=0x7ffd6d199928 sp=0x7ffd6d199920 pc=0x46c505\n: unknown" 

日志内容:日志中包含 docker exec -it oat /bin/bash 执行时的报错内容,除此以外,没有其他信息。

查看 meminfo 信息

代码语言:javascript
复制
# cat /proc/meminfo
MemTotal:        3880632 kB
MemFree:         2978048 kB
MemAvailable:          0 kB
Buffers:            2856 kB
Cached:           222120 kB
SwapCached:            0 kB
Active:           657516 kB
Inactive:         126644 kB
Active(anon):     559364 kB
Inactive(anon):    18312 kB
Active(file):      98152 kB
Inactive(file):   108332 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:        559184 kB
Mapped:            97404 kB
Shmem:             18492 kB
Slab:              54756 kB
SReclaimable:      30844 kB
SUnreclaim:        23912 kB
KernelStack:        4224 kB
PageTables:        10128 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     3880632 kB
Committed_AS:    4424840 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       15564 kB
VmallocChunk:   34359715580 kB
HardwareCorrupted:     0 kB
AnonHugePages:      8192 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      114544 kB
DirectMap2M:     3031040 kB
DirectMap1G:     3145728 kB

发现 MemFree 还有 2978048KB(2GB 多),但 MemAvailable 为 0KB 。HugePages_Total 为 0,不需要考虑大页占用内存的情况。

meminfo 中 MemFree 和 MemAvailable 的区别

  • MemFree 指当前空闲的页(空闲的页面不代表可以直接使用),而 MemAvailable 用于判断系统是否有足够内存来启动新程序。
  • MemAvailable 由内核计算而来,大概公式为:

MemAvailable ≈ MemFree + 可回收的 PageCache + 可回收的 Slab(SReclaimable) - 内存水位线(LowWatermark) - 预留内存

分析

我们发现虽然 /proc/meminfo 中的 MemFree 为 2978048KB(超过 2GB),但 MemAvailable 为 0KB,所以在执行 docker exec -it oat /bin/bash 命令时,报了 out of memory

根据 MemAvailable 的计算公式,怀疑是 内存水位线定高了,导致计算出来的 MemAvailable 为 0。

内存水位线说明

Linux 内存管理使用水位线机制来防止系统陷入完全不可用的状态。

内存水位线会影响内存的回收行为:

  • 内存水位线(最低水位线):当内存低于 min 时,直接回收(Direct Reclaim),新进程分配内存时会被阻塞以回收内存;
  • 内存水位线(低水位线):当内存低于 low 时,kswapd 守护进程被唤醒,开始异步回收内存;
  • 内存水位线(高水位线):当内存大于 high 时,kswapd 停止回收内存。

值的大小限制:

min < low < high

查看内存水位线

代码语言:javascript
复制
# awk '/min/ {sum += $2} END {print sum * 4 " KB"}' /proc/zoneinfo
2097148 KB

# sysctl vm.min_free_kbytes
vm.min_free_kbytes = 2097152  

我们发现内存的最低水位线的值为 2097152KB,达到了 2GB,而我们的内存总量一共才 3880632KB(约 4GB),显然 vm.min_free_kbytes 配置的设置不合理。

解决方案

调小 vm.min_free_kbytes 的值,设置为 256MB。

代码语言:javascript
复制
# 临时配置
sysctl -w vm.min_free_kbytes=262144  

# 永久配置
name_sysctl="vm.min_free_kbytes"
line="vm.min_free_kbytes=262144"
file="/etc/sysctl.conf"
grep -w -i -q  ${name_sysctl} ${file} && 
    sed -i "s#${name_sysctl}[ ]*=.*#${line}#g" ${file} || 
    echo "${line}" >> ${file}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 bisal的个人杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
    • 报错操作
    • 报错内容
    • 查看容器状态
    • 查看容器的资源限制
    • 查看 messages 信息,确认是否存在 OOM 日志
    • 查看 meminfo 信息
    • meminfo 中 MemFree 和 MemAvailable 的区别
  • 分析
    • 内存水位线说明
    • 查看内存水位线
  • 解决方案
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档