C++堆内存过度分配:如何禁用交换内存触发std::bad_alloc?
在Windows 10中让C++内存分配失败时触发std::bad_alloc异常
问题原因
Windows 10默认采用虚拟内存过度承诺和懒加载分配机制:
new操作仅预留虚拟地址空间,不会立刻分配物理内存,直到首次访问内存页时才提交。- 物理内存耗尽后,系统会自动使用交换文件扩展可用内存,因此常规的
new很难触发std::bad_alloc。
解决方案
通过以下修改,强制程序仅使用物理内存,并让内存分配失败时立刻抛出异常:
方法1:限制进程工作集+启用new异常模式
修改代码,结合Windows API限制进程不能使用交换文件,并配置new的异常行为:
#include <iostream> #include <Windows.h> #include <cstdlib> #include <cstring> int main() { // 获取物理内存大小(单位:KB),限制进程工作集不超过物理内存 SIZE_T physicalMemKB = GetPhysicallyInstalledSystemMemory() / 1024; if (!SetProcessWorkingSetSizeEx( GetCurrentProcess(), 0, physicalMemKB, QUOTA_LIMITS_HARDWS_MIN_ENABLE )) { std::cerr << "Failed to configure working set: " << GetLastError() << "\n"; return EXIT_FAILURE; } // 启用new操作符的异常模式,分配失败时抛出std::bad_alloc _set_new_mode(1); try { while (true) { int* ptr = new int[10000000L]; // 写入数据触发内存提交,避免懒加载导致的虚假分配 memset(ptr, 0, sizeof(int) * 10000000L); } } catch (const std::bad_alloc& e) { std::cout << "Allocation failed: " << e.what() << "\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; }
方法2:自定义new操作符
直接替换默认的new实现,使用VirtualAlloc立刻提交物理内存,分配失败时主动抛出异常:
#include <iostream> #include <Windows.h> #include <cstdlib> #include <new> // 自定义new操作符 void* operator new(size_t size) { void* ptr = VirtualAlloc( NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); if (!ptr) { throw std::bad_alloc(); } return ptr; } // 匹配的delete操作符 void operator delete(void* ptr) noexcept { VirtualFree(ptr, 0, MEM_RELEASE); } int main() { // 可选:限制进程工作集,进一步确保不使用交换 SIZE_T physicalMemKB = GetPhysicallyInstalledSystemMemory() / 1024; SetProcessWorkingSetSizeEx( GetCurrentProcess(), 0, physicalMemKB, QUOTA_LIMITS_HARDWS_MIN_ENABLE ); try { while (true) { int* ptr = new int[10000000L]; } } catch (const std::bad_alloc& e) { std::cout << "Allocation failed: " << e.what() << "\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; }
关键说明
SetProcessWorkingSetSizeEx的QUOTA_LIMITS_HARDWS_MIN_ENABLE参数强制进程的内存不能被分页到交换文件,物理内存耗尽时分配直接失败。_set_new_mode(1)让标准库的new在底层调用HeapAlloc时启用异常模式,替代默认的返回NULL行为。memset(或任何内存写入操作)是为了触发Windows的内存提交机制,否则仅预留地址空间不会消耗实际物理内存。
内容的提问来源于stack exchange,提问作者Ketrix
相关产品推荐
相关产品推荐

