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

.NET中如何防止内存映射RAM页面被Windows卸载?

如何让内存映射文件的页面始终驻留在Windows RAM中?

针对你的问题——通过内存映射处理80GB+只读数据,闲置后Windows会主动卸载RAM中的页面(仅任务计划程序运行时出现),可以通过以下几种方法解决:

方法一:用Win32 API锁定内存页面(最彻底)

Windows默认允许将闲置的内存页面换出到磁盘,要强制保留,需要用VirtualLock锁定映射的内存区域,但前提是进程要有**“锁定页面在内存中”**的权限。

步骤1:配置权限

  • 打开secpol.msc(本地安全策略)
  • 展开「本地策略」→「用户权限分配」
  • 找到「锁定页面在内存中」,添加运行程序的用户账户
  • 如果是通过任务计划程序运行,确保任务使用的账户已在列表中,同时勾选任务属性里的「使用最高权限运行」

步骤2:在C#中调用VirtualLock

先声明Win32 API的P/Invoke:

using System.Runtime.InteropServices;
using System.Diagnostics;

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualLock(IntPtr lpAddress, UIntPtr dwSize);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualUnlock(IntPtr lpAddress, UIntPtr dwSize);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetProcessMemoryPriority(IntPtr hProcess, uint priority);

然后在你的内存映射代码后添加锁定逻辑:

long filesize = new FileInfo(path).Length;
file = MemoryMappedFile.CreateFromFile(path, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
accessor = file.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
mapping = new MemoryMapped();
handle = accessor.SafeMemoryMappedViewHandle;
handle.AcquirePointer(ref ptr);
mapping._handle = handle;
mapping._pointer = ptr;
mapping._size = filesize;

// 锁定内存页面
bool lockSuccess = VirtualLock(ptr, (UIntPtr)filesize);
if (!lockSuccess)
{
    int errorCode = Marshal.GetLastWin32Error();
    Console.WriteLine($"锁定内存失败,错误码:{errorCode}");
    // 常见错误:权限不足(错误码1314),此时需要检查权限配置
}

// 同时设置进程内存优先级,增强效果
SetProcessMemoryPriority(Process.GetCurrentProcess().Handle, 0x00000002); // PROCESS_MEMORY_PRIORITY_HIGH

注意事项

  • 不再使用内存映射时,必须调用VirtualUnlock释放锁定,避免内存泄漏:
VirtualUnlock(mapping._pointer, (UIntPtr)mapping._size);
handle.ReleasePointer();
accessor.Dispose();
file.Dispose();
  • 如果你的RAM不足以容纳整个80GB文件,VirtualLock会失败,此时需要调整策略(比如只锁定常用区域)

方法二:定期访问内存页面(备选,无需特殊权限)

如果无法配置锁定权限,可以后台定期遍历内存区域,让Windows认为页面是活跃的,避免被换出:

using System.Threading.Tasks;
using System.Threading;

// 启动后台线程
Task.Run(() =>
{
    while (true)
    {
        unsafe
        {
            byte* p = (byte*)mapping._pointer;
            // 每4KB访问一个字节(覆盖所有页面)
            for (long i = 0; i < mapping._size; i += 4096)
            {
                _ = p[i]; // 仅读取,不做其他操作
            }
        }
        Thread.Sleep(TimeSpan.FromMinutes(2)); // 每隔2分钟执行一次
    }
});

这个方法会占用少量CPU资源,但胜在无需系统权限配置。


内容的提问来源于stack exchange,提问作者Rob The Quant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:35:39