std::bad_alloc仅在标准库明确要求抛异常的分配操作中抛出:new表达式(无nothrow)、vector/string::reserve、make_unique/make_shared及显式调用operator new(非nothrow)失败时;malloc等返回空指针,不抛此异常。

std::bad_alloc 在哪些分配操作里会抛出?
它只在 C++ 标准库明确要求“分配失败时必须抛异常”的场景下触发,不是所有 malloc 失败都会变成 std::bad_alloc。关键看调用的是哪一层接口:
-
new表达式(不带nothrow)——最常见来源,比如new int[1000000000]超出可用内存时 -
std::vector::reserve()或std::string::reserve()等容器预分配内部缓冲区失败时 -
std::make_unique/std::make_shared底层调用new失败时 - 显式调用
operator new(非operator new nothrow)失败时
注意:malloc、realloc、operator new(std::nothrow) 这些失败都返回空指针或 nullptr,**不会**抛 std::bad_alloc。
为什么有时候 OOM 了却没抛 std::bad_alloc?
因为分配器行为和编译器实现有关,尤其在资源耗尽边界上表现不一致:
- Linux 默认启用 overcommit(
/proc/sys/vm/overcommit_memory = 1),new可能成功返回地址,但第一次真正写入时触发Segmentation fault或被 OOM Killer 杀掉进程——这时根本没机会走到std::bad_alloc - 某些嵌入式 STL 实现或自定义分配器可能禁用异常,把
new降级为返回 nullptr(需确认是否定义了_GLIBCXX_USE_CXX11_ABI和异常开关) - 链接时用了
-fno-exceptions,所有throw都被编译器忽略,程序直接 abort 或未定义行为
所以不能假设“内存不够 → 一定 catch 到 std::bad_alloc”,得结合系统策略和编译选项一起看。
立即学习“C++免费学习笔记(深入)”;
捕获 std::bad_alloc 的实际建议
它不是用来兜底“程序快崩了”的万能异常,而是用于局部可控的资源申请失败处理:
- 只在明确知道某次分配可能失败、且有替代路径时才 try/catch,比如尝试分配大缓存失败后切回流式处理
- 不要在 main() 外层无差别 catch(...),
std::bad_alloc不代表可恢复,更不代表其他异常也能这么搞 - 避免在析构函数里 new —— 析构中抛
std::bad_alloc会导致std::terminate,因为栈展开期间再抛异常是未定义行为 - 如果用
std::vector::resize(),注意它内部先 allocate 再 construct,allocate 失败才抛std::bad_alloc;construct 失败则抛构造函数自己的异常
示例:
try {
auto buf = std::make_unique(1_GiB);
} catch (const std::bad_alloc& e) {
// 这里能拿到的只是分配失败信号,不代表整个进程还有多少余量
log("fallback to mmap or disk buffer");
}
和 operator new(nothrow) 混用时的坑
混用带异常和不带异常的分配方式容易误判失败原因:
-
new (std::nothrow) T返回nullptr,而new T抛std::bad_alloc—— 二者语义不同,不能靠是否抛异常来统一判断“内存是否紧张” - 自定义全局
operator new若没实现nothrow版本,或实现里忘了 throw,会导致行为不一致 - 第三方库(如 Eigen、OpenCV)内部可能用 nothrow 分配,你 catch
std::bad_alloc完全捕不到它们的失败
真要监控内存压力,得靠 mallinfo、malloc_stats 或 cgroup memory limit 配合信号(SIGUSR1 等),而不是依赖异常抛出时机。









