Windows进程实际内存占用疑问:为何1GB分配内存工作集仅占约80%
Windows 10内存分配与工作集机制解析
平台特性
- CPU:Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz 1.80 GHz
- 内存:8GB
- 环境:Windows 10、Visual Studio、MSVC 编译器
我用C++编写了一段内存分配与读写代码,通过.NET的System.Diagnostics.Process类监控进程的内存统计信息,代码如下:
#include <iostream> #include <chrono> #include <thread> int main() { long long n = 1'000'000'000; std::cout << "part 1 started" << std::endl; char* arr = static_cast<char*>(malloc(n)); std::this_thread::sleep_for(std::chrono::milliseconds(500)); arr[n - 1] = rand(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "part2 started" << std::endl; for (long long i = n / 5.0 * 4.0; i < n; i++) arr[i] = rand(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "part3 started" << std::endl; for (long long i = n/5; i < n; i++) arr[i] = rand(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); long long tmpll = 0; for (int i = 0; i < 100000000; i++) { tmpll = 0; if (rand() % 2) tmpll = 2'400'000'000; if (rand() % 2) tmpll += 2'400'000'000; if (rand() % 2) tmpll = 2'400'000'000; tmpll += rand(); tmpll += rand(); if (tmpll < 0) tmpll = abs(tmpll); tmpll %= n; arr[tmpll] = rand(); } std::cout << arr[rand() % n] << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::cout << "ended" << std::endl; free(arr); std::cout << "freed" << std::endl; }
观察到的现象
监控PrivateMemorySize64、WorkingSet64和PeakWorkingSet64后发现:
PrivateMemorySize64与申请的1GB内存量一致,但PeakWorkingSet64始终小于该值(通常不超过分配空间的80%)。- 禁用交换文件并重启后,依然得到类似结果:
PrivateMemorySize64 = 1002602496 WorkingSet64 = 803287040 PeakWorkingSet64 = 803291136
- 重新启用交换文件后,结果无明显变化:
PrivateMemorySize64 = 1002516480 WorkingSet64 = 803246080 PeakWorkingSet64 = 803299328
我原以为随机写入数组各个位置会迫使系统为全部1GB内存分配物理存储,但工作集峰值始终达不到1GB,需要解释Windows 10在此场景下的实际机制。
核心机制解析
虚拟内存的延迟分配
Windows采用请求分页机制:malloc仅在进程的虚拟地址空间中预留1GB连续地址范围,不会立刻分配对应物理内存。只有当程序实际读写某页内存时,系统才会触发页错误,为该页分配物理内存。代码中的未访问页面
你的代码从未触及数组的前n/5区域(即200MB):
- part2写入了数组的后1/5区域,part3写入了后4/5区域,后续随机写入也不会覆盖前1/5的所有页面。
- 工作集(Working Set)统计的是进程实际占用的物理内存页面,未被访问的虚拟页不会占用物理内存,自然不会计入WorkingSet或PeakWorkingSet。
禁用交换文件的影响
禁用交换文件仅限制了系统将物理页换出到磁盘的能力,但延迟分配机制依然生效。未被访问的虚拟页不需要物理内存,所以即使没有交换文件,这部分内存也不会被分配,工作集自然无法达到1GB。PrivateMemorySize64的定义
PrivateMemorySize64统计的是进程已提交的虚拟内存总量(即通过malloc等调用向系统承诺使用的内存),不管是否实际分配了物理内存,这就是它与工作集数值差异的核心原因。
验证方法
若要让PeakWorkingSet达到1GB,只需在代码中遍历整个数组并写入每个位置:
// 在free(arr)前添加 for (long long i = 0; i < n; i++) { arr[i] = 0; // 触发所有页的物理内存分配 }
此时再观察PeakWorkingSet64,数值会接近1GB。
内容的提问来源于stack exchange,提问作者vev01
相关产品推荐
相关产品推荐

