5

我正在运行一个 C++ 程序,该程序std::bad_alloc在任意点终止,这取决于指定的输入。以下是有关该计划的一些观察/要点:

  • 对于较短的运行(运行时间取决于输入),程序正常完成。问题只出现在较大的运行中。
  • 该程序没有任何可检测到的内存泄漏。使用 Valgrind/Memcheck 对较小的运行进行了检查。此外,我的整个代码没有任何指针(所有动态分配都由库完成,例如 instd::vectorstd::string;失败的是这些库类内部的分配),因此内存泄漏的可能性极小。
  • 几个对象在循环中分配,然后移动到容器中。其中一些对象打算在程序几乎结束之前一直存在。
  • 我怀疑堆碎片可能是一个问题(请参阅C++ 程序因 std::bad_alloc 而死,但 valgrind 报告没有内存泄漏)但我使用的是 64 位系统,带有 64 位编译器(特别是带有 g++ 的 Linux)和堆碎片在 64 位土地上让我相信堆碎片在 64 位系统上不是问题。

还有什么我应该尝试的吗?任何可以提供帮助的特定工具?还有其他建议吗?

ulimit -v更新:终于发现虚拟内存在早些时候已经受到限制。我后来忘记了这一点,因此内存耗尽。将其重新设置以unlimited解决问题。

4

2 回答 2

6

std::bad_alloc表示您请求的内存超出了可用内存。

您可能会遇到程序没有泄漏但仍然真正耗尽内存的情况:

vector<long> v;
long n = 0;
for(;;)
{
   v.push_back(n++);
}

最终将耗尽您拥有的任何机器上的所有可用内存 - 但它没有泄漏 - 所有内存都在向量中计算。显然,任何容器都可以做完全相同的事情,vector, list, map, 并不重要。

Valgrind 只查找您“放弃”分配的实例,而不是您正在使用当前可访问内存填充系统的实例。

LIKELY 正在发生的是上述情况的一种较慢的形式 - 您在某个容器中存储的越来越多。它可能是您正在缓存的东西,或者当您认为已经删除它时您没有删除的东西。

观察应用程序上的内存量实际上是在某些监控程序中使用(Linux/Unix 中的“top”,Windows 中的“task manager”),并查看它是否真的增长了。如果是这种情况,那么您需要弄清楚什么在增长——对于一个大型程序来说,这可能很棘手(有些事情可能应该增长,而其他事情则不会......)

当然,你也有可能突然得到一些错误的计算,例如要求负数的元素T* p = new T[elements];会导致错误的分配,因为元素被转换为无符号数,而负数无符号数是巨大的。

如果您可以在调试器中捕获 bad_alloc,则通常很容易发现这种情况,因为请求的大量数据new将非常明显。

在调试器中捕获异常通常会有所帮助,尽管当它出错时您当然可能只是为一个小字符串分配内存,如果您确实有泄漏的东西,这就是分配时的情况并不罕见它出错了。

如果您使用的是 Unix 风格,您还可以使用ulimit -m size(以千字节为单位)或ulimit -v size.

于 2013-08-18T21:24:10.927 回答
1

std::bad_alloc也可能意味着您请求的数据量为负数,即使机器中有足够的内存也是如此。

当我使用常规有符号整数(仍然是 32 位)而不是长整数(64 位)来指定数组计数时,这种情况很容易在我的 64 位 linux 机器上发生,并且我将两个太大的数字相乘为了得到最终的计数。乘法的结果在 2.147Gig 处悄悄溢出,因此可能变为负数。

比如说你想在 21 维空间中分配 1 亿个点。没问题。计数为 2,100,000,000。现在将维度大小增加到 22,它会从悬崖上掉下来。这很容易用 printf 验证:

int N = 100000000;
int D = 22;
int count = N * D;
printf("count = %'d\n", count);

count = -2,094,967,296

并且std::bad_alloc因为请求的内存计数为负而启动。

Ed.:我在评论中注意到这似乎是一个不可重现的结果,因为现在 new[count]std::bad_array_new_length在重新启动机器后给出。也就是说,代码仍然不正确并中断,但给出的错误信息与之前不同。在任何一种情况下都不要这样做。

于 2019-12-04T21:00:55.130 回答