linux下应读取/proc/self/status的vmpeak(历史最大虚拟内存,kb)和vmsize(当前总量,kb),并结合getrlimit(rlimit_as)获取虚拟地址空间硬限制,三者协同判断内存压力。

Linux 下用 /proc/self/status 读取 VmPeak 和 VmSize
Linux 进程的虚拟内存使用情况不靠 C++ 标准库提供,得直接解析 /proc/self/status。这个文件里有两行关键字段:VmPeak(历史最大虚拟内存用量)和 VmSize(当前虚拟内存总量),单位都是 KB。
实操时注意:该文件是内核动态生成的,每次读取都反映实时状态;但解析需自己写逻辑,不能依赖第三方库。
- 用
std::ifstream打开/proc/self/status,逐行查找以VmPeak:或VmSize:开头的行 - 用
std::stoi提取冒号后首个整数(跳过空格和单位),别直接std::stoll读整行——后面可能有KB字符干扰 - 若读取失败(如容器环境 /proc 不可用、权限不足),
VmPeak值应视为无效,不要 fallback 到 0
getrlimit(RLIMIT_AS, &rlim) 获取虚拟内存软硬限制
真正意义上的“临界值”其实是进程能申请到的虚拟地址空间上限,由 RLIMIT_AS 决定。它和 VmPeak 不同:前者是系统施加的硬性边界,后者只是统计值。
调用前必须包含 <sys></sys>,且注意 rlimit.rlim_cur 是软限制(可被程序自行提升,直到硬限制),rlimit.rlim_max 是硬限制(仅 root 可调)。
- 若
rlimit.rlim_cur == RLIM_INFINITY,说明没设限,理论上可增长到地址空间上限(如 64 位下接近 128TB,但受实际物理内存+swap 影响) - 某些容器(如 Docker 默认配置)会把
RLIMIT_AS设为RLIM_INFINITY,但通过 cgroups 限制实际可用内存——此时getrlimit返回的不是真实瓶颈 - 在 fork 子进程前获取该值,因为子进程继承父进程的 rlimit,但后续
setrlimit不影响已存在的子进程
Windows 上没有等价的“虚拟内存分配临界值”概念
Windows 没有统一的进程级虚拟内存配额机制。GetProcessMemoryInfo 能拿到 PeakPagefileUsage(换页文件峰值)或 PeakVirtualSize(虚拟地址空间峰值),但这些仍是统计量,不是可配置的临界阈值。
真正起约束作用的是:进程架构(32 位 vs 64 位)、是否启用 /LARGEADDRESSAWARE、系统保留的内核地址空间、以及当前可用的分页文件大小——它们共同构成隐式上限,无法用单个 API 查询。
- 32 位程序默认只有 2GB 用户态虚拟地址空间;加了
/LARGEADDRESSAWARE且系统开启 3GB 开关后,才可能到 3GB -
PeakVirtualSize接近 4GB(32 位)或远超 1TB(64 位)时,不代表马上 OOM,只说明地址空间快耗尽——实际崩溃往往先因VirtualAlloc返回NULL或抛std::bad_alloc - 不要尝试用
GlobalMemoryStatusEx的ullTotalVirtual当作“剩余虚拟内存”,它返回的是整个系统的用户态虚拟地址空间总量,对单进程无意义
为什么不能用 malloc 失败来反推临界值
很多开发者想靠反复 malloc 直到失败来探测上限,这不可靠。现代 glibc 的 malloc 默认使用 brk + mmap 混合策略,小块内存走堆,大块直接 mmap —— 后者失败可能是因为 mmap 区域碎片化,而非总虚拟内存耗尽。
- 即使
malloc(1ULL 失败,<code>VmSize可能才几百 MB,因为mmap要求连续虚拟地址空间 - 启用
MALLOC_ARENA_MAX=1环境变量可减少多 arena 导致的虚假碎片,但无法消除根本问题 - 更糟的是,
new抛std::bad_alloc时,你已失去控制权——无法区分是虚拟内存不足、物理内存不足、还是分配器内部异常
真正要监控虚拟内存压力,得组合看 VmSize(增长趋势)、RLIMIT_AS(硬边界)、以及 mmap 返回值(每次大内存申请)。三者缺一不可,少一个就容易误判临界点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











