bad alloc — ошибки выделения памяти в C ++ без использования новых

У меня проблемы с моей программой, вызывающей большое количество исключений выделения памяти, и мне очень трудно диагностировать проблему … Я бы опубликовал код, но моя программа очень большая, и у меня есть проблемы с частной информацией, поэтому я в надежде получить помощь без публикации кода. Если вы планируете ответить с помощью какой-либо формы комментария SSCCE, просто прекратите читать сейчас и сэкономите нам обоим некоторое время. Это тот случай, когда я не могу опубликовать краткий код — я постараюсь быть максимально ясным и лаконичным с описанием моей проблемы и некоторыми конкретными вопросами.

Фон программы — моя программа в основном обработчик данных. Он принимает в качестве входных данных несколько таблиц данных, выполняет для них вычисления и выводит новые таблицы данных на основе результатов расчетов. Все мои структуры данных являются пользовательскими классами (состоящими из типов int, double и string с векторными контейнерами для массивов). Во всех случаях я инициирую экземпляры переменных класса без использования new и delete.

описание проблемы — моя программа компилируется без предупреждений и отлично работает на небольших наборах данных. Однако, как только я увеличиваю набор данных (с массива 20×80 до 400×80), я начинаю выдавать исключения bad_alloc (как только я обработал первые 35 записей или около того). Большие наборы данных работают нормально в 17 из моих 18 модулей — я выделил одну функцию, где происходят ошибки. Расчеты, необходимые для этой функции, приведут к созданию около 30 000 строк данных, тогда как другие функции в моем коде генерируют более 800 000 строк без инцидентов.
Единственный реальный уникальный атрибут в этом модуле — это то, что я много использую изменение размера (примерно 100 раз за вызов функции), и что функция использует рекурсивные циклы во время операции изменения размера (функция выделяет квадратные футы из здания одного арендатора в время, а затем обновление оставшихся футов, которые должны быть распределены после моделирования размера и продолжительности аренды каждого арендатора, пока не будут выделены все квадратные футы). Кроме того, ошибка происходит почти в одном и том же месте каждый раз (но не в одном и том же месте, потому что у меня есть генератор случайных чисел, который вносит некоторые изменения в результаты). Что меня действительно смущает, так это то, что первые ~ 34 вызова этой функции работают нормально, и вызов ~ 35 не требует больше памяти, чем предыдущие 34, но у меня все же есть эти исключения bad_alloc для 35-го вызова …

Я знаю, что трудно помочь без кода. Пожалуйста, просто попробуйте указать мне направление. Мои конкретные вопросы следующие:

  1. Если я не использую «new» и «delete», и все мои переменные инициализируются ВНУТРИ локальных функций, возможно ли возникновение проблем утечки / выделения памяти из-за повторных вызовов функций? Есть ли что-нибудь, что я могу или должен сделать, чтобы управлять памятью при инициализации переменных, включающих локальную функцию, используя «vector Instance;» объявить мои переменные?

  2. Есть ли вероятность того, что у меня не хватает памяти стека, если я делаю всю программу через стек? Возможно ли мне загрузить некоторые из моих больших таблиц поиска (карты и т. Д.) В кучу, а затем просто использовать стек для моих итераций, где важна скорость?

  3. Есть ли проблема с использованием изменения размера, связанного с памятью? Может ли это быть случай, когда я должен использовать «new» и «delete» (во многих случаях меня предупреждали не использовать их, если нет очень веской, конкретной причины для этого)?

  4. [Относится к 3] В рамках проблемной функции я создаю переменную класса, а затем перезаписываю эту переменную примерно 20 раз (один раз для каждой «итерации» моей модели). Мне не нужны данные из предыдущей итерации, когда я делаю это … поэтому я мог бы создать новый экземпляр переменной для каждой итерации, но я не понимаю, как это обязательно поможет (так как, очевидно, я могу сделать все 20 итераций на одном экземпляре на первых ~ 34 срезах данных)

Любые мысли будут оценены. Я могу попытаться опубликовать некоторый код, но я уже пробовал это однажды, и все, казалось, были отвлечены тем фактом, что он не компилируется. Я могу опубликовать соответствующую функцию, но она сама не компилируется.

Вот класс, который вызывает проблему:

// 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

Решение

bad_alloc возникает из-за проблем с кучей и может быть вызван любым кодом, который косвенно выделяет или освобождает кучу памяти, которая включает в себя все стандартные коллекции библиотек (std::vector, std::mapи т. д.), а также std::string,

Если ваши программы не используют много памяти в куче (поэтому они не исчерпывают кучу), bad_allocСкорее всего, они вызваны повреждением кучи, которое обычно вызывается использованием висячих указателей в куче.

Вы упоминаете, что ваш код делает много resize операции — resize в большинстве коллекций аннулирует все итераторы в коллекции, поэтому, если вы повторно используете любой итератор после resizeчто может привести к куче коррупции bad_alloc исключения. Если вы используете непроверенный доступ к элементам вектора (std::vector::operator[]), и ваши индексы находятся вне диапазона, что также может привести к повреждению кучи.

Лучший способ отследить повреждение кучи и ошибки памяти в целом — использовать отладчик кучи, такой как Valgrind

6

Другие решения

Классы как std::vector а также std::string разрешено бросать bad_alloc или другие исключения. В конце концов, они должны использовать некоторую память, которая исходит откуда-то, и на любом компьютере достаточно памяти.

Стандарт 17.6.5.12/4:

Операции-деструкторы, определенные в стандартной библиотеке C ++, не должны вызывать исключения. Каждый деструктор в стандартной библиотеке C ++ должен вести себя так, как если бы у него была спецификация без исключения. Любые другие функции, определенные в стандартной библиотеке C ++, которые не имеют Исключение-спецификация может генерировать определенные реализацией исключения, если не указано иное. [Сноска 1] Реализация может усилить это неявное Исключение-спецификация добавив явный.

Сноска 1: В частности, они могут сообщить об ошибке при выделении памяти, выдав исключение типа bad_allocили класс, полученный из bad_alloc (18.6.2.1). Реализации библиотеки должны сообщать об ошибках, генерируя исключения или получаемые из стандартных классов исключений (18.6.2.1, 18.8, 19.2).

3

Если я не использую «new» и «delete», и все мои переменные инициализируются ВНУТРИ локальных функций, возможно ли возникновение проблем утечки / выделения памяти из-за повторных вызовов функций?

Неясно. Если все переменные, на которые вы ссылаетесь, являются локальными, нет. Если вы используете malloc (), calloc () и free (), да.

Есть ли вероятность того, что у меня не хватает памяти стека, если я делаю всю программу через стек?

Нет, если вы получите bad_alloc. Если вы получили ошибку «переполнение стека», да.

Возможно ли мне загрузить некоторые из моих больших таблиц поиска (карты и т. Д.) В кучу, а затем просто использовать стек для моих итераций, где важна скорость?

Что ж, трудно поверить, что вам нужна локальная копия таблицы поиска в каждом кадре стека рекурсивного метода.

Есть ли проблема с использованием изменения размера, связанного с памятью?

Конечно. Вы можете убежать.

Может ли это быть случай, когда я должен использовать «новый» и «удалить»

Сегодня невозможно, не зная больше о ваших структурах данных.

(Во многих случаях меня предупреждали не использовать их, если для этого нет очень веской, конкретной причины)?

Кем? Зачем?

Внутри проблемной функции я создаю переменную класса,

Вы создаете экземпляр класса в стеке. Я думаю. Просьба уточнить.

затем перезаписываю эту переменную около 20 раз (один раз для каждой «итерации» моей модели).

С заданием? Есть ли в классе оператор присваивания? Это правильно? Использует ли сам класс кучную память? Правильно ли он распределяется и удаляется при строительстве, разрушении и назначении?

2

Поскольку, как вы сказали, вы используете std::vector с распределителем по умолчанию, проблема возникает, когда вы используете много std::vector::resize(...) и это происходит после нескольких итераций, я думаю, что вы столкнулись с проблемой фрагментации кучи.

2