函数不会抛出bad_alloc异常

5
我正在尝试完成 Stroustrup 的 C++PL4 书中的一个练习。任务是:
分配大量内存使用 "new",以引发 "bad_alloc" 异常。报告分配了多少内存以及花费了多少时间。执行两次:一次不写入分配的内存,一次写入每个元素。
以下代码未抛出 "std::bad_alloc" 异常。执行程序后在终端中会看到 "Killed" 消息。
此外,以下代码退出约 4 秒钟。但是,当我取消注释内存使用消息时,程序却崩溃了。
// ++i;
// std::cout << "Allocated " << i*80 << " MB so far\n";

程序将运行几分钟。一段时间后,它会打印出已分配的大量内存,但我在系统监视器应用程序中没有看到太多变化。为什么会这样?

我使用Linux和系统监视器应用程序来查看使用情况。

#include <iostream>
#include <vector>
#include <chrono>

void f()
{
    std::vector<int*> vpi {};
    int i {};
    try{
        for(;;){
            int* pi = new int[10000];
            vpi.push_back(pi);
            // ++i;
            // std::cout << "Allocated " << i*80 << " MB so far\n";
        }       
    }
    catch(std::bad_alloc){
        std::cerr << "Memory exhausted\n";
    }
}

int main() {
    auto t0 = std::chrono::high_resolution_clock::now();
    f();
    auto t1 = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t0-t1).count() << " ms\n";
}

那是我的直觉。但为什么它打印出来,比如说,迄今已分配了几太字节的内存? - user4833046
1
在现代残酷的世界中,调用new(以及malloc或者是brk())并不一定会分配内存。它只是通过一系列层次向操作系统发送请求,而操作系统则分配一个虚拟内存区域(舍入到页面)。因此,只有访问给定页面才会执行内存分配。此外,现代操作系统允许“超额提交”,即分配更多的内存供不同应用程序之和使用,即使具有交换也一样。这是因为很少所有应用程序同时需要所有已分配的内存,并且可以按顺序服务它们的实际需求。 - user3159253
1
在最糟糕的情况下,当应用程序确实访问它们的内存时,一个名为OOM Killer的特殊系统服务就会出现并杀死应用程序(几乎是随机的 :))。因此,依赖于bad_alloc确实是一个坏主意,它可能会被引发,也可能不会,这取决于当前操作系统设置和应用程序在应用执行时的行为。 - user3159253
1
为了增加分配虚拟页面的机会,您可以访问刚分配的元素,但这仍然不能保证完全成功,只是提高了可能性。 - user3159253
1
还要查看这篇文章:http://opsmonkey.blogspot.ru/2007/01/linux-memory-overcommit.html - user3159253
显示剩余3条评论
1个回答

4
在现代残酷的世界中,调用new(以及malloc()甚至brk())不一定会分配内存。它只是通过一系列层次向操作系统发送请求,而操作系统则分配一个虚拟内存区域(舍入为系统内存页)。因此,只有对给定内存的后续访问才执行实际分配。
此外,现代操作系统允许内存“过度承诺”。有时(取决于操作系统及其设置),应用程序可以要求完全超出操作系统甚至理论上可分配的所有内存,包括所有交换区等,而没有任何显着问题。例如,请查看此页面
这样做是因为在现实生活中,所有应用程序实际上同时使用所有分配的内存的情况相当不可能。更常见的情况是,在99.99..%的时间里,应用程序仅使用它们的部分内存,并且按顺序执行,因此操作系统有机会无缝地服务它们的请求。
为了增加机会实际上导致内存分配错误,您可以访问刚刚分配的元素,但我不会称其为逐字保证,只是“增加可能性”。
在最坏的情况下,当这样的操作系统实际上发现它无法分配足够(虚拟)内存,因为太多应用程序同时请求访问它们看似已分配的数据时,操作系统内存管理器会启动一种特殊过程称为“OOM killer”,它只是启发式地杀死(=随机:)选择的应用程序。
因此,依赖于bad_alloc现在是一个坏主意。有时您可以可靠地收到它(例如,通过ulimit/setrlimit人为限制您的应用程序),但通常您的应用程序将在不保证任何内容的环境中运行。只要不成为内存狂魔并为其余部分祈祷:)

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