我有足够的RAM,但是在启动和完成大量进程后,似乎大多数应用程序的虚拟内存已经被换出到磁盘,而切换回任何旧进程需要很长时间将内存重新加载到RAM。
是否有一种方法可以通过Windows API或内核调用让Windows取消分页所有(或尽可能多的)内存?也许通过遍历运行中的进程列表并让内存管理器取消分页每个进程的内存?
更新3:我已将完整的程序上传到Github。
根据迄今为止的回复,这里有一个天真的建议,尝试将所有应用程序都放回物理内存中:
假设你有2GB的RAM,只有1GB实际上被进程所需。如果所有内容都在物理内存中,你只需要复制256个块,不是世界末日。最终,很有可能所有进程现在都完全在物理内存中。
可能的便利和优化选项:
我可以使用EnumProcesses()遍历所有进程;如果有任何关于如何逐块复制整个进程的内存的建议,我将不胜感激。
更新:这是我的示例函数。它以进程ID作为其参数,并从该进程的每个好页面复制一个字节。(第二个参数是最大进程内存大小,可通过GetSystemInfo()获取。)
void UnpageProcessByID(DWORD processID, LPVOID MaximumApplicationAddress, DWORD PageSize)
{
MEMORY_BASIC_INFORMATION meminfo;
LPVOID lpMem = NULL;
// Get a handle to the process.
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
// Do the work
if (NULL == hProcess )
{
fprintf(stderr, "Could not get process handle, skipping requested process ID %u.\n", processID);
}
else
{
SIZE_T nbytes;
unsigned char buf;
while (lpMem < MaximumApplicationAddress)
{
unsigned int stepsize = PageSize;
if (!VirtualQueryEx(hProcess, lpMem, &meminfo, sizeof(meminfo)))
{
fprintf(stderr, "Error during VirtualQueryEx(), skipping process ID (error code %u, PID %u).\n", GetLastError(), processID);
break;
}
if (meminfo.RegionSize < stepsize) stepsize = meminfo.RegionSize;
switch(meminfo.State)
{
case MEM_COMMIT:
// This next line should be disabled in the final code
fprintf(stderr, "Page at 0x%08X: Good, unpaging.\n", lpMem);
if (0 == ReadProcessMemory(hProcess, lpMem, (LPVOID)&buf, 1, &nbytes))
fprintf(stderr, "Failed to read one byte from 0x%X, error %u (%u bytes read).\n", lpMem, GetLastError(), nbytes);
else
// This next line should be disabled in the final code
fprintf(stderr, "Read %u byte(s) successfully from 0x%X (byte was: 0x%X).\n", nbytes, lpMem, buf);
break;
case MEM_FREE:
fprintf(stderr, "Page at 0x%08X: Free (unused), skipping.\n", lpMem);
stepsize = meminfo.RegionSize;
break;
case MEM_RESERVE:
fprintf(stderr, "Page at 0x%08X: Reserved, skipping.\n", lpMem);
stepsize = meminfo.RegionSize;
break;
default:
fprintf(stderr, "Page at 0x%08X: Unknown state, panic!\n", lpMem);
}
//lpMem = (LPVOID)((DWORD)meminfo.BaseAddress + (DWORD)meminfo.RegionSize);
lpMem += stepsize;
}
}
CloseHandle(hProcess);
}
问题:我递增的区域大小是否最多只包含一页,还是我错过了其他页面?我应该尝试找出页面大小,然后仅递增区域大小和页面大小的最小值吗? 更新2:页面大小仅为4KiB!我将上述代码更改为每次递增4KiB。在最终代码中,我们将摆脱循环内的fprintf。
实现起来并不难。使用VirtualQueryEx()
发现进程使用的虚拟地址,使用ReadProcessMemory()
强制页面重新加载。
这样做可能不会有任何区别,只会导致您的程序需要花费更长的时间来完成其工作。页面重新加载缓慢的常见诊断是分段的页面文件。例如在Windows XP上普遍存在,当硬盘长时间未进行碎片整理,并且经常充满时。SysInternals'的PageDefrag实用程序可以帮助解决问题。
VirtualLock
将会做到接近你所需的功能,而使用SetProcessWorkingSetSize。参见kb108449。 - sehe不,Windows本身没有提供这样的功能。像Cacheman和RAM IDLE这样的程序通过简单地分配大块内存来实现此目的,迫使其他东西分页到磁盘,从而有效地实现了您想要的效果。