64位进程过度分配内存导致Windows冻结。

3
当我在Windows(7及以上版本)上运行64位程序,并且该程序尝试分配过多内存(接近物理内存的100%),系统将停滞不前。如果我没有及时捕获并杀死有问题的进程,系统将变得无响应,需要硬重启。显然,程序不应该尝试分配这么多内存,但是错误可能会发生,我想保护其他正在运行的进程免受测试/调试代码中任何此类错误的影响。(在32位中这不是太大的问题,因为逻辑内存限制提供了一个防止系统范围内内存窒息的安全网。)
所以我有两个问题:
1. 进程可以限制其内存消耗吗?在Windows中是否有系统调用可以设置内存限制,以便超出限制的分配失败而不是崩溃整个系统? 2. 或者,是否有任何方法可以设置系统范围的每个进程内存限制或为特定应用程序设置限制?
这是一个导致我的计算机冻结的示例代码。它使用Visual Studio 10.0编译为x86_amd64配置,并在具有16 GB RAM的Windows 8.1笔记本电脑上运行。它尝试分配32 GB的内存。自行决定是否运行。
int main(void)
{
    const static int csNumArrays=10, csArraySize=800000000;
    int i, j, **p;

    p=new int*[csNumArrays];
    for (i=0; i<csNumArrays; ++i)
    {
        p[i]=new int[csArraySize];
        for (j=0; j<csArraySize; ++j)
            p[i][j]=j;
    }
    return 0;
}

https://blogs.technet.microsoft.com/clinth/2012/10/11/can-a-process-be-limited-on-how-much-physical-memory-it-uses/ - Hans Passant
@HansPassant 谢谢,但这不是我需要的。只是想澄清一下:我不是试图操纵进程的物理内存限制(也称为“工作集”),我正在尝试限制进程的虚拟内存大小。当源代码可用时(例如通过系统调用)或在源代码不可用时通过管理工具从外部进行尝试。 - user3204459
@HarryJohnston 感谢您的回复。您说它不应该导致整个系统崩溃,您自己试过吗?我在三个不同的场合遇到了这个问题,所有情况都涉及不同的软件、不同版本的 Windows 和完全不同的机器。在 Linux 上也发生过这种情况,但我知道如何解决。我几乎不相信这只是巧合。 - user3204459
@HarryJohnston 感谢Harry的帮助。也许在8GB的机器上使用12GB并不足以使其承受压力。尝试将其加倍或三倍。无论如何,我添加了我的代码,它会导致计算机冻结。刚刚尝试了一下,由于计算机无响应而不得不进行硬重启,持续了30分钟。即使最终它会恢复,我认为应该清楚为什么我想要实施某种安全措施,以防止流氓程序在长时间内禁用计算机,特别是如果那个流氓程序是我的(不完美的)程序。 - user3204459
我的Windows 7测试机在运行你的程序时只会偶尔变得无响应。但Windows 8可能会受到更严重的影响。 - Harry Johnston
2个回答

4

可能最好的选择是让该进程将自己放入一个Windows job并应用ProcessMemoryLimit选项。 (一个问题:如果该进程已经在作业中,例如因为它正在作为启动脚本或计划任务运行,则此方法无效。)您还可以使用相同的技术来限制启动的另一个进程的内存使用情况(或者已经在运行)。

另外,您可以尝试启动一个线程,使用CreateResourceMemoryNotification来检测低物理内存条件并终止进程。 但您可能会发现这容易出现误报。(这绝对不应该留在生产代码中。)


3

基于Harry Johnston的有用回答,我编写了一段代码,可以在程序开始时添加,以将程序的内存分配限制为物理内存大小的因子。

ULONGLONG aMemSize;
HANDLE aJob=::CreateJobObject(NULL, NULL);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION aLimit;

    // Get physical memory and set process memory limit to 75% of physical memory
GetPhysicallyInstalledSystemMemory(&aMemSize);
aMemSize*=size_t(768);                      // convert kb to bytes and apply 75% factor
memset(&aLimit, 0, sizeof(aLimit));
aLimit.BasicLimitInformation.LimitFlags=JOB_OBJECT_LIMIT_PROCESS_MEMORY;
aLimit.ProcessMemoryLimit=aLimit.PeakProcessMemoryUsed=aLimit.JobMemoryLimit=
                                                aLimit.PeakJobMemoryUsed=aMemSize;
::SetInformationJobObject(aJob, JobObjectExtendedLimitInformation, &aLimit, sizeof(aLimit));
::AssignProcessToJobObject(aJob, ::GetCurrentProcess());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接