我正在研究相当大的SIP电话应用程序,偶尔当我们在大量调用加载时使用集成的Web UI(使用tntnet编写)时,程序将因为抛出std :: bad_alloc而退出。有数百个线程在使用(每个活动调用3个),因此导致异常的代码位置非常随机,但总是在使用GUI之后。
现在,我知道在内存不足时可以抛出std :: bad_alloc,在这种情况下不是这种情况。我也在想,当存在堆损坏时可能会抛出它,我仍然在寻找它在代码库中的位置。
但我的问题是,除了内存不足或堆损坏之外,还有其他原因会抛出std :: bad_alloc吗?我在Linux上使用GNU g ++。
答案 0 :(得分:6)
最有可能的是,你真的是内存不足。这将是一个非常罕见的堆损坏错误,一直导致仅 bad_alloc被抛出。这就像手术精确的涂鸦一样。
代码中只有一个错误可能会分配大量内存。但是你会期望在这个代码中抛出异常,至少是很长一段时间。异常来自许多不同的地方的事实与此相反。
严重碎片可能会导致问题,尤其是对于malloc
实施不佳的平台。这种情况很少见,但确实发生了。
我立即做的一件事 - 捕获异常并调用保存/proc/self/maps
副本的函数。这将使您了解该过程的峰值内存使用情况。您可以判断它是否接近任何平台,策略或硬件限制。
答案 1 :(得分:5)
在Linux上,当前的地址空间限制可用于人为地限制进程可以使用的内存量。您可以使用setrlimit(RLIMIT_AS, ...)
手动设置此项。也可以使用bashrc
在ulimit -v
中为整个shell设置此项。这也可以在/etc/security/limits.conf
中为整个系统设置。这个地方甚至可能有一个/ proc / sys条目,我不确定。
如果达到地址空间限制,则在尝试分配更多内存时,您的进程将抛出std :: bad_alloc。在64位系统上,这可以是一个很好的“安全”,以确保错误的应用程序或库不会因可用内存而耗尽,并使系统完全交换或停止工作。确保程序没有在某个地方设置它,并确保环境的其余部分也没有设置它。您可以在程序中间的某个位置插入一些代码来调用getrlimit(RLIMIT_AS, ...)
,以确保它没有在某个地方偷偷摸摸。
一个或许更常见的原因(当然,除了实际耗尽内存之外)是一个无符号整数环绕大小写,其中uin32_t或uint64_t用于分配内存但是为0并从中减去1,导致一个非常大的请求分配(64位,即数千PB)。
无论如何,跟踪此问题的最佳方法是使用GDB。如果您的应用程序根本不使用异常(因此根本没有“catch”语句),那么您可以启用核心文件(ulimit -c unlimited
)。下次程序崩溃时,操作系统将生成一个核心文件,并在GDB中加载它会立即给你一个回溯,显示程序崩溃的位置。
如果你有几个(但不是很多)使用try的地方并捕获这些错误的alloc,除了在你调试这个问题时将它们注释掉,你可以在GDB中运行应用程序并使用{{1每次抛出异常时都会让GDB中断。要使其中任何一个工作,请不要使用catch throw
进行编译,并始终使用-fomit-frame-pointer
进行编译(即使使用-O3
)。