程序以std::bad_alloc终止

5
我正在运行一个C++程序,它在任意点上都会因为std::bad_alloc而崩溃,这取决于指定的输入。以下是关于程序的一些观察/要点:
  • 对于较短的运行时间(运行时间取决于输入),程序正常完成。问题只出现在更长的运行中。
  • 程序没有任何可检测到的内存泄漏。这在较短的运行中使用Valgrind/Memcheck进行了检查。此外,我的整个代码没有任何指针(所有动态分配都由库完成,例如在std::vector和std::string中进行分配;失败的是这些库类中的分配),因此内存泄漏极不可能发生。
  • 几个对象在循环中被分配,然后移动到容器中。其中几个对象的寿命打算一直保持到程序接近结束。
  • 我怀疑堆碎片可能是一个问题(参见C++ program dies with std::bad_alloc, BUT valgrind reports no memory leaks),但我使用的是64位编译器的64位系统(具体为Linux与g++),Heap fragmentation in 64 bit land让我相信在64位系统上,堆碎片不可能成为一个问题。

还有什么其他尝试的方法吗?有哪些特定的工具可以帮忙?有没有其他建议?

更新:最终发现早先通过ulimit -v限制了虚拟内存。我后来忘记了这一点,因此出现了内存耗尽的问题。将其设置为unlimited即可解决问题。


检查基本的堆栈破坏、缓冲区溢出或错误返回。这些是valgrind可能会忽略的问题,可能会导致损坏问题。 - Jesus Ramos
同意,我在那部分做得不多。你有什么工具可以推荐吗? - r.v
我通常会手动进行处理,获取一些线索的简单方法是将其加载到gdb中,一旦它遇到bad_alloc,则向上遍历堆栈并检查垃圾值以及可能来自哪里。 - Jesus Ramos
2个回答

6

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

有时候程序没有泄漏,但仍然真正耗尽了内存:

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

最终会耗尽您所拥有的任何机器中的所有可用内存-但它并没有泄漏-向量中的所有内存都已计算。显然,任何容器都可以做到完全相同的事情,无论是vectorlist还是map

Valgrind只能找到您“放弃”分配的实例,而不是填充当前可达内存的系统。

可能发生的是上述情况的较慢形式-您正在某个容器中存储越来越多的内容。这可能是您正在缓存的内容,或者是您认为已将其删除但实际上没有删除的内容。

观察应用程序实际使用的内存量,在某些监视程序(Linux / Unix中的“top”,Windows中的“任务管理器”)中查看其是否实际增长。如果是这种情况,则需要找出增长的原因-对于大型程序,这可能很棘手(有些东西可能应该增长,而其他东西则不应该...)

当然,也有可能您突然得到了一些错误的计算,例如在T* p = new T [elements];中请求负数元素会导致bad alloc,因为elements转换为无符号数字,并且负无符号数字是巨大的。

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

在调试器中捕获异常通常应该有所帮助,尽管当出现错误时,您可能只是为小字符串分配内存,如果您确实有一些泄漏,则在出现错误时分配内存的情况并不罕见。

如果您正在使用Unix的某个版本,还可以通过使用ulimit -m size(以千字节为单位)或ulimit -v size将应用程序允许使用的内存量设置为较小的大小,以加快错误查找速度。


谢谢!问题有点幼稚。几天前,我通过“ulimit -v”将虚拟内存限制在约4GB左右(你的答案提醒了我!)。现在,我在同一个终端中运行此程序,忘记了这个设置,因此出现了问题。将其设置为无限制就可以解决问题了。正如我在问题中所说,我在64位系统上不使用指针,因此你指出的其他问题不太可能发生。也许这个问题和答案将来会帮助其他人! - r.v
如果您没有使用指针[间接地,例如通过std::vector],那么您不会得到std::bad_alloc]。您确定对于您的应用程序使用超过4GB是正确的吗?我不是说它不是,我只是想确保您已经考虑过了。如果您理解为什么需要更多内存来完成应用程序,则增加内存是可以的。很多年前,我遇到一个非常恶心的错误,结果是一个缓慢的泄漏 - 我们增加了内存,它运行了24小时。然后我们加速一点,突然它在24小时内就开始耗尽内存了... - Mats Petersson
我正在进行的计算将会占用大量内存,但很可能我可以在一定程度上降低内存利用率。一旦代码功能完备,我将在不久的将来进行调查。 - r.v

1

std::bad_alloc可能意味着您正在请求负数的数据量,即使机器上有足够的内存。

在我的64位linux机器上,当我使用普通的signed int(仍为32位)而不是long int(64位)来指定数组计数,并且我乘以两个过大的数字以获取最终计数时,就会很容易出现这种情况。乘法的结果在2.147Gig处悄然溢出,因此可能变成负数。

例如,假设您想在21维空间中分配100 million个点。没问题。计数为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错误。

注:在评论中指出,这似乎是一个无法重现的结果,因为重新启动机器后,现在的new[count]会产生std::bad_array_new_length。也就是说,代码仍然不正确并且会崩溃,但给出的错误消息与之前不同。无论哪种情况都不要这样做。


实际上,现在看起来这个确切的情况会导致 std::bad_array_new_length。唉。 - DragonLord
显然,新的[N*D]可能实际上已经用尽了交换空间,因为计算机上运行了太多其他进程。重新启动到一个干净的机器似乎已经解决了这个问题。结果可能会有所不同。 - DragonLord

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