【问题标题】:How can I know the real maximum size of a vector? (Not using std::vector::max_size)我怎样才能知道向量的实际最大大小? (不使用 std::vector::max_size)
【发布时间】:2020-05-18 18:04:00
【问题描述】:

在一个在线课程中,我正在学习矢量。在其中一个示例中,他们解释说:std::vector::max_size() 应该给我向量可以达到的最大大小。我决定测试一下:

#include <iostream>
#include <exception>
#include <vector>

int main(void) {
    std::vector <int> nums;
    int max = nums.max_size();
    std::cout << "Max: " << max << std::endl;
    for (int i = 0; i < max; i++) {
        try {
            nums.push_back(i);
        }
        catch (std::bad_alloc ex) {
            std::cerr << ex.what() << std::endl;
            std::cout << "Failed at: " << i << std::endl;
            break;
        }
    }

    return 0;
}

这是运行它的结果:

Max: 1073741823
bad allocation
Failed at: 204324850

短 869416973 个整数。

所以我开始用谷歌搜索它。 Here 我读到它返回“容器可以达到的最大潜在大小”,并补充说“但绝不保证容器能够达到那个大小”。我会想象它会失败,但不会失败。在失败之前它只完成了 1/5。为什么std::vector::max_size 如此关闭?我认为更重要的是,有没有办法真正知道向量的潜在大小?

【问题讨论】:

  • 向量中的元素个数与大小不同。 sizeof 以字节为单位
  • 最大可实现大小取决于您有多少可用 RAM,并且您可用的 RAM 量可能在一毫秒之间变化很大(因为其他程序会分配和释放 RAM 块);因此,没有简单的方法可以返回一个有意义的值,即向量可能此时在您的硬件上能够增长多大
  • max_size() 用于容器很奇怪,而且不是特别有用。事后看来,它可能不应该存在。
  • @JeremyFriesner:RAM 应该是虚拟内存,对吧? RAM 的数量并不重要,只是程序会因为交换而变慢。
  • @ThomasWeller 我使用的许多计算机都故意配置为没有任何交换空间,因为由于交换而无法跟上其任务负载的计算机甚至不如必须在某些进程上调用 OOM-killer 以恢复 RAM。至少在后一种情况下,被杀死的进程可以自动重生,然后可以在没有人工干预的情况下恢复有用的工作。所以我认为 RAM 的数量绝对很重要 :)

标签: c++ vector


【解决方案1】:

注意max_size 函数返回的元素的最大数量,它没有说明所需的内存量。

如果我们假设sizeof(int) == 4(很常见),那么204324850 元素将需要817299400 字节的连续 内存(几乎是780 MiB)。

您会收到 bad_alloc 异常,因为向量根本无法分配足够的内存来容纳所有元素。

【讨论】:

  • 此外,数据结构簿记会占用内存,并且对于像这样的小数据元素,它可能很重要。但我认为vector 只会有恒定的开销,因为它只是在内存中连续分配元素,因此不需要额外的指针等。
  • 不应该 max_size 是一个常数,因为它返回一个理论最大值?哪个永远是一样的?
  • @TimotejLeginus 它是定义的实现。它可能取决于标准库之外的因素,例如操作系统、目标硬件和其他因素。
【解决方案2】:

std::vector::max_size() 应该给我向量可以达到的最大尺寸

这并不完全正确。 max_size 为您提供理论上限。 Vector 肯定不支持任何大于该大小的大小,但这并不一定意味着您可以创建所有大小达到该大小的向量。

最大的限制因素是操作系统愿意或能够为进程分配的可用内存量。没有标准的方法来获得这个大小,甚至实现特定的方法也不是直截了当的。

另一个潜在的限制是最长的空闲连续地址空间,它可能是碎片的。对于地址空间巨大的 64 位程序,这可能不是问题,但对于 32 位或更小地址的系统来说,这是一个考虑因素。


失败于:204324850

假设 4 字节 int,即大约 780 兆字节(非公制)。


总结:与其试图找出程序在运行时可以使用多少内存,不如算出你知道足够的内存量。不要分配更多。确保计算机有足够的内存,并且操作系统未配置为将内存使用限制为较少量。使用 64 位地址空间。

【讨论】:

  • 有趣的事实:reserve() 不会弄脏它分配的内存,因此您只需要虚拟地址空间,而不需要后备存储(物理 RAM + 交换空间)来实际处理它。大多数操作系统允许“过度使用”,因此如果您的系统没有足够的可用 RAM,您的程序可能会在 push_back 期间崩溃到已保留的空间。
  • @PeterCordes 很有趣,C++ 标准是否可以在程序在push_back 上崩溃?
  • IDK 如果它严格符合 ISO C++ 标准,但即使您不首先 reserve,任何不是非常大的分配通常都会成功(在过度使用的操作系统上),然后在你尝试使用它。这包括当 std::vector 在无保留地增长时尝试分配 + 复制。所以整个std::bad_alloc 异常模型并不真正匹配现代主流操作系统在实践中的工作方式,当你受到有限的 RAM + 交换而不是地址空间的限制时(即地址空间几乎是 64 位代码的正常情况)与 RAM 大小相比不受限制,大型服务器除外。)
  • @Paul C++ 标准承认存在有限的实现限制,这可能会阻止实现成功处理程序。只要语言实现文件证明内存使用有限制(嗯,呃),据我所知,崩溃不会违反一致性。
  • 关于 Windows,来自 Raymond:“系统 [...] 进行了足够的记账,以确保如果您将数据写入您提交的页面,那么系统将有一个地方保存这些数据并按需生产”,所以听起来不应该发生。 devblogs.microsoft.com/oldnewthing/20170419-00/?p=95995
【解决方案3】:

这样想; vector 的编写方式可以在内部处理多达(例如)32 位的元素,因此 max_size 会给你一些大约 2-40 亿范围内的数字。 但是你是在一个只有 1MB 内存的系统上运行代码,所以你当然不可能把容器扩大到那么大。但是,vector 无法知道您使用的是什么系统 - 它只知道它的最大理论限制。

【讨论】:

    【解决方案4】:

    我会指出以下因素导致无法分配max_size 的内存量:

    • 您系统上的可用内存可能在调用max_size 和尝试分配内存之间发生了变化
    • 可能没有足够大的连续内存块可用于分配(即总共可能有足够的内存,但已碎片化,并且向量保证将内存存储在连续块中)李>
    • 向量在添加新元素时调整大小(就像你对push_back所做的那样);发生这种情况时,旧向量的内容会被复制到新向量 - 并且可能没有足够的内存来同时存储它们。
    • 首先,您的系统中可能没有足够的内存。例如,在我的系统上,max_size 返回类似 (maximum_value_of_ptrdiff_t/sizeof(vector_element)) 的内容。虽然这个数字可能很大,但给定机器的物理限制可能允许更小的东西

    【讨论】:

    • alloc+copy 将失败的事实不会阻止您首先分配一个大的vector&lt;&gt;。它只是调用new;它不要求分配器检查是否有空间用于另一个相同或双倍大小的分配。或者您是说 OP 使用 push_back 而不是一个大的 resize 来增长他们的向量的方式意味着他们的测试会发现一个低于他们最初使用 resize()reserve() 的限制- 空向量?因为是的,这是真的。
    • @PeterCordes 是的,我的意思是后者。我当然不是说 vector 会检查未来是否有可能进行分配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-28
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多