不能只调用一次virtualalloc完事,因为mem_reserve仅预留虚拟地址空间(不占物理资源,读写会崩溃),而mem_commit才真正分配物理页或页文件;二者分离可避免过早消耗物理内存,支持按需提交、大块连续地址管理及内存分页优化。

VirtualAlloc 的 MEM_RESERVE 和 MEM_COMMIT 不是“两种内存”,而是同一块虚拟地址空间的两个独立操作阶段——先预留地址,再真正分配物理资源。
为什么不能只调用一次 VirtualAlloc 就完事?
Windows 的虚拟地址空间(32 位下约 2GB 用户区)是稀疏的、按页(4KB)和区域(64KB 粒度)管理的。直接 MEM_COMMIT 会强制系统立即关联物理内存或页文件,但你可能根本没打算立刻用;而只 MEM_RESERVE 则只是在进程的虚拟地址表里“画个圈”,不占物理资源,也不触发缺页异常。
-
MEM_RESERVE:仅登记一段连续的、未被占用的虚拟地址范围,返回的指针可合法用于后续VirtualAlloc或VirtualFree,但此时读写会触发ACCESS_VIOLATION -
MEM_COMMIT:必须基于已RESERVE的地址(或同时指定两者),此时系统才真正绑定物理页/页文件,首次访问不会崩溃 - 常见错误:
VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE)中p是未保留过的地址 → 返回NULL,不是“失败重试”,而是非法参数
MEM_RESERVE | MEM_COMMIT 组合的实际意义
这是最常用的惯用写法,等效于“一步到位申请可用内存”。但它仍经历两个内部动作:先查空闲区域并标记为已保留,再立即提交。关键点在于:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 它不等于
malloc或new:没有堆管理开销,不走 CRT 堆,适合大块、长期、对齐敏感的内存(如帧缓冲、大数组缓存) - 它不自动清零:
PAGE_READWRITE提交后内容是未定义的,需手动memset或用PAGE_WRITECOPY+VirtualProtect控制 - 若只保留不提交,
GlobalMemoryStatus显示的dwAvailVirtual会减少,但dwAvailPhys和dwAvailPageFile几乎不变
Reserve 后分批 Commit 的典型场景
当你需要一块超大连续地址(比如 512MB),又不想一次性耗尽物理内存或页文件时,可先保留整段,再按需提交子区域:
- 游戏引擎加载大型地图:先
VirtualAlloc(NULL, 512*1024*1024, MEM_RESERVE, PAGE_NOACCESS)占位;实际渲染某区块时,再对对应偏移调用VirtualAlloc(pBase + offset, chunkSize, MEM_COMMIT, PAGE_READWRITE) - 避免碎片:用
MEM_TOP_DOWN从高地址向下分配,降低与 DLL 加载冲突概率(DLL 默认加载在中低地址) - 注意:多次
MEM_COMMIT必须落在同一MEM_RESERVE区域内,且起始地址需对齐到sysInfo.dwAllocationGranularity(通常是 64KB)
释放时必须匹配 Reserve/Commit 状态
VirtualFree 的行为取决于你传入的地址和标志:
- 若释放的是仅
RESERVE的区域:用VirtualFree(p, 0, MEM_RELEASE),size必须为 0,整块保留区域被归还 - 若释放的是
RESERVE | COMMIT的区域:可用MEM_DECOMMIT先解提交(释放物理资源但保留地址),之后再MEM_RELEASE归还地址 - 常见坑:
VirtualFree(p, size, MEM_RELEASE)中size非 0 → 失败;VirtualFree传入非VirtualAlloc返回的地址 → 未定义行为
真正容易被忽略的,是 Reserve 和 Commit 的粒度差异:地址对齐看 dwAllocationGranularity(64KB),而实际内存页大小是 dwPageSize(4KB)。Commit 可以按页粒度做,但 Reserve 必须按分配粒度对齐——哪怕你只想要 1 字节,系统也会给你划出至少 64KB 的虚拟地址空间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










