如何在Windows系统中创建超大型稀疏数组?
在Windows实现类Linux mmap的稀疏数组行为
Linux下通过mmap(MAP_PRIVATE | MAP_ANONYMOUS)可以创建稀疏数组:仅写入的页会分配物理内存,未写入的页读取直接返回0。在Windows上可以通过匿名内存映射实现完全一致的行为,无需占用磁盘空间,具体方案如下:
核心方案说明
使用CreateFileMapping配合INVALID_HANDLE_VALUE创建匿名内存映射,这种映射不关联任何磁盘文件,完全基于系统内存。Windows的「按需零页」机制会自动处理:
- 读取未写入的页时,直接返回0,不会触发内存访问异常
- 只有当写入页时,系统才会分配对应的物理内存
解决你之前的问题
- 用
VirtualAlloc仅保留未提交内存时,读取会触发异常,因为未提交的页没有绑定任何物理资源;而匿名映射的页是已「逻辑提交」的,由系统按需分配物理页 - 之前使用
CreateFileMapping时如果传入了实际文件句柄,会占用磁盘空间;改用INVALID_HANDLE_VALUE即可创建纯内存的匿名映射
对应实现代码
#include <windows.h> #include <iostream> const size_t MB = 1024 * 1024; const size_t GB = MB * 1024; int main() { size_t alloc_size = 512 * MB; size_t total_alloc = 100 * GB; size_t num_allocs = total_alloc / alloc_size; std::cout << "Allocating virtual memory...\n"; char* base = nullptr; // 分块创建匿名内存映射,模拟原Linux代码的行为 for (size_t i = 0; i < num_allocs; ++i) { // 创建匿名内存映射对象 HANDLE hMap = CreateFileMapping( INVALID_HANDLE_VALUE, // 匿名映射,不关联文件 nullptr, // 默认安全属性 PAGE_READWRITE, // 读写权限 static_cast<DWORD>(alloc_size >> 32), // 高32位大小 static_cast<DWORD>(alloc_size & 0xFFFFFFFF), // 低32位大小 nullptr // 匿名映射无需名称 ); if (hMap == nullptr) { std::cerr << "CreateFileMapping failed, error: " << GetLastError() << "\n"; return 1; } // 映射视图到进程地址空间 char* addr = static_cast<char*>(MapViewOfFile( hMap, FILE_MAP_READ | FILE_MAP_WRITE, // 读写权限 0, 0, alloc_size )); CloseHandle(hMap); // 映射后可以关闭句柄,不影响视图 if (addr == nullptr) { std::cerr << "MapViewOfFile failed, error: " << GetLastError() << "\n"; return 1; } base = addr; // 记录最后一块的基地址(原代码假设连续,实际Windows不保证,但行为一致) std::cout << "Mapped address: " << static_cast<void*>(addr) << " | Block: " << i << "\n"; } std::cout << "Total virtual memory allocated: " << total_alloc / GB << " GB\n"; std::cout << "Base address: " << static_cast<void*>(base) << "\n"; std::cout << "Press enter to start reading pages...\n"; std::cin.get(); std::cout << "Reading first byte of each page...\n"; // 遍历所有页,读取首字节(未写入的页会返回0) for (size_t i = 0; i < total_alloc; i += 0x1000) { char x = base[i]; // 可以添加打印验证,比如每1000页打印一次值 // if (i % (0x1000 * 1000) == 0) std::cout << "Page " << i/0x1000 << " value: " << static_cast<int>(x) << "\n"; } std::cout << "Done!\n"; std::cin.get(); // 注意:实际代码中需要UnmapViewOfFile释放映射,这里为了简化省略 return 0; }
关键细节说明
- 匿名映射的特性:
CreateFileMapping传入INVALID_HANDLE_VALUE时,创建的是系统分页文件支持的匿名映射,不会占用用户磁盘空间,仅在需要物理内存时从分页文件分配。 - 按需零页:Windows会自动为未写入的映射页提供零填充,读取时直接返回0,无需手动处理。
- 内存释放:使用
UnmapViewOfFile释放映射的视图,系统会自动回收未使用的物理内存。
内容的提问来源于stack exchange,提问作者Chris_F
相关产品推荐
相关产品推荐

