You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}

关键细节说明

  1. 匿名映射的特性:CreateFileMapping传入INVALID_HANDLE_VALUE时,创建的是系统分页文件支持的匿名映射,不会占用用户磁盘空间,仅在需要物理内存时从分页文件分配。
  2. 按需零页:Windows会自动为未写入的映射页提供零填充,读取时直接返回0,无需手动处理。
  3. 内存释放:使用UnmapViewOfFile释放映射的视图,系统会自动回收未使用的物理内存。

内容的提问来源于stack exchange,提问作者Chris_F

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:59:52