.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
相关产品推荐
相关产品推荐

