5

我的程序抛出大量内存分配异常时遇到问题,我很难诊断问题...我会发布代码,但我的程序非常大而且我有专有信息问题,所以我希望在不发布代码的情况下获得一些帮助。如果您打算回复某种形式的 SSCCE 评论,请立即停止阅读,为我们俩节省一些时间。在这种情况下,我无法发布简洁的代码 - 我会尽量让我的问题描述和一些具体问题尽可能简洁明了。

程序背景- 我的程序基本上是一个数据处理器。它将一堆数据表作为输入,对它们进行计算,并根据计算结果吐出新的数据表。我所有的数据结构都是用户定义的类(由 int、double 和 string 类型以及数组的向量容器组成)。在所有情况下,我都在不使用 new 和 delete 的情况下启动类变量的实例。

问题描述- 我的程序在没有警告的情况下编译,并且在较小的数据集上运行良好。但是,一旦我增加了数据集(从 20x80 数组到 400x80),我开始抛出 bad_alloc 异常(一旦我处理了前 35 个左右的条目)。大型数据集在我的 18 个模块中的 17 个中运行良好——我已经隔离了一个发生错误的函数。此函数所需的计算将导致创建大约 30,000 行数据,而我的代码中的其他函数会生成 800,000+ 行而不会发生意外。
这个模块中唯一真正独特的属性是我经常使用调整大小(每个函数调用大约 100 次),并且该函数在调整大小操作期间使用递归循环(该函数将平方英尺分配给一个租户在一个时间,然后在模拟每个租户的租约大小和持续时间后更新要分配的剩余英尺,直到分配完所有平方英尺)。此外,错误每次都发生在几乎相同的位置(但不是完全相同的位置,因为我有一个随机数生成器,它会对结果产生一些变化)。真正让我感到困惑的是,对该函数的第一次 ~34 次调用工作正常,并且 ~35 次调用不需要比前 34 次更多的内存,但是我在第 35 次调用时遇到了这些 bad_alloc 异常......

我知道没有代码很难提供帮助。请试着给我一些方向。我的具体问题如下:

  1. 如果我没有使用“new”和“delete”,并且我的所有变量都在本地函数的内部初始化,是否有可能通过重复的函数调用出现内存泄漏/分配问题?在使用“向量实例”初始化变量包括本地函数时,我可以或应该做些什么来管理内存?声明我的变量?

  2. 如果我通过堆栈执行整个程序,是否有可能我的堆栈内存不足?是否有可能我需要在堆上加载一些大型查找表(地图等),然后只在速度很重要的地方使用堆栈进行迭代?

  3. 使用与内存相关的大量调整大小是否有问题?这可能是我应该使用“new”和“delete”的一个例子吗(在很多情况下我都被警告不要使用它们,除非有非常强烈的具体理由这样做)?

  4. [与 3 相关] 在问题函数中,我正在创建一个类变量,然后重写该变量大约 20 次(我的模型的每个“迭代”一次)。当我这样做时,我不需要上一次迭代的数据......所以我表面上可以为每次迭代创建一个变量的新实例,但我不明白这将如何帮助(因为显然我能够在前约 34 个数据切片上的一个实例上进行所有 20 次迭代)

任何想法将不胜感激。我可以尝试发布一些代码,但我已经尝试过一次,每个人似乎都被它不可编译的事实分心了。我可以发布有问题的函数,但它不会自行编译。

这是导致问题的类:

// Class definition
class SpaceBlockRentRoll
{
public:
    double RBA;
    string Tenant;
    int TenantNumber;
    double DefaultTenantPD;
    int StartMonth;
    int EndMonth;
    int RentPSF;
    vector<double> OccupancyVector;
    vector<double> RentVector;
};

// Class variable declaration (occuring inside function)
vector<SpaceBlockRentRoll> RentRoll;

此外,这是发生递归的函数的片段

for (int s=1; s<=NumofPaths; ++s) {
    TenantCounter = 0;
    RemainingTenantSF = t1SF;
    if (RemainingTenantSF > 0) {
        while (RemainingTenantSF > 0) {
            TenantCounter = TenantCounter + 1;

            // Resize relevant RentRoll vectors
            ResizeRentRoll(TenantCounter, NumofPaths, NumofPeriods, RentRoll);

            // Assign values for current tenant
            RentRoll[TenantCounter] = AssignRentRollValues(MP, RR)  
            // Update the square feet yet to be allocated
            RemainingTenantSF = RemainingTenantSF - RentRoll[TenantCounter].RBA;
        }
    }
}
4

4 回答 4

8

bad_alloc来自某种堆问题,并且可以被任何间接分配或释放堆内存的代码抛出,其中包括所有标准库集合(std::vectorstd::map等)以及std::string.

如果您的程序不使用大量堆内存(因此它们没有用完堆),bad_alloc则 s 可能是由堆损坏引起的,这通常是由于在堆中使用悬空指针引起的。

你提到你的代码做了很多resize操作——resize在大多数集合上都会使集合上的所有迭代器失效,所以如果你在 a 之后重用任何迭代器resize,这可能会导致堆损坏并显示bad_alloc异常。如果您使用未经检查的向量元素访问 ( std::vector::operator[]),并且您的索引超出范围,这也可能导致堆损坏。

一般来说,跟踪堆损坏和内存错误的最佳方法是使用堆调试器,例如valgrind

于 2013-07-30T22:40:19.737 回答
4

std::vectorstd::string允许抛出bad_alloc或其他异常。毕竟,他们必须使用来自某个地方的一些内存,而任何计算机都只有这么多内存可以运行。

标准 17.6.5.12/4:

C++ 标准库中定义的析构函数操作不得抛出异常。C++ 标准库中的每个析构函数都应该表现得好像它有一个非抛出异常规范。除非另有说明,否则在 C++ 标准库中定义的任何其他没有异常规范的函数都可能引发实现​​定义的异常。[脚注 1] 一个实现可以通过添加一个显式的异常规范来加强这个隐含的异常规范。

脚注 1:特别是,他们可以通过抛出 type 的异常bad_alloc或从bad_alloc(18.6.2.1) 派生的类来报告分配存储失败。库实现应该通过抛出标准异常类(18.6.2.1、18.8、19.2)的异常或派生自标准异常类来报告错误。

于 2013-07-30T22:19:55.013 回答
2

如果我没有使用“new”和“delete”,并且我的所有变量都在本地函数的内部初始化,是否有可能通过重复的函数调用出现内存泄漏/分配问题?

不清楚。如果您引用的所有变量都是本地变量,则不是。如果您使用 malloc()、calloc() 和 free(),是的。

如果我通过堆栈执行整个程序,是否有可能我的堆栈内存不足?

如果你得到 bad_alloc,就不会。如果您遇到“堆栈溢出”错误,是的。

是否有可能我需要在堆上加载一些大型查找表(地图等),然后只在速度很重要的地方使用堆栈进行迭代?

好吧,很难相信在递归方法的每个堆栈帧中都需要查找表的本地副本。

使用与内存相关的大量调整大小是否有问题?

当然。你可以用完。

这可能是我应该使用“新”和“删除”的例子吗

如果不了解更多关于您的数据结构,今天是不可能的。

(我在很多情况下都被警告过不要使用这些,除非有非常强烈的、具体的理由这样做)?

通过谁?为什么?

在问题函数中,我正在创建一个类变量,

您正在堆栈上创建该类的实例。我认为。请澄清。

然后重写该变量大约 20 次(我的模型的每个“迭代”一次)。

有任务?该类是否有赋值运算符?这是正确的吗?类本身是否使用堆内存?在构造、销毁和分配时是否正确分配和删除?

于 2013-07-30T22:21:21.307 回答
2

正如您所说,因为您使用std::vector的是默认分配器,所以当您使用大量时会std::vector::resize(...)出现问题,并且在一些迭代后会出现问题,我的猜测是您遇到了堆碎片问题。

于 2013-07-30T22:38:33.930 回答