У меня проблемы с моей программой, вызывающей большое количество исключений выделения памяти, и мне очень трудно диагностировать проблему … Я бы опубликовал код, но моя программа очень большая, и у меня есть проблемы с частной информацией, поэтому я в надежде получить помощь без публикации кода. Если вы планируете ответить с помощью какой-либо формы комментария 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-го вызова …
Я знаю, что трудно помочь без кода. Пожалуйста, просто попробуйте указать мне направление. Мои конкретные вопросы следующие:
Если я не использую «new» и «delete», и все мои переменные инициализируются ВНУТРИ локальных функций, возможно ли возникновение проблем утечки / выделения памяти из-за повторных вызовов функций? Есть ли что-нибудь, что я могу или должен сделать, чтобы управлять памятью при инициализации переменных, включающих локальную функцию, используя «vector Instance;» объявить мои переменные?
Есть ли вероятность того, что у меня не хватает памяти стека, если я делаю всю программу через стек? Возможно ли мне загрузить некоторые из моих больших таблиц поиска (карты и т. Д.) В кучу, а затем просто использовать стек для моих итераций, где важна скорость?
Есть ли проблема с использованием изменения размера, связанного с памятью? Может ли это быть случай, когда я должен использовать «new» и «delete» (во многих случаях меня предупреждали не использовать их, если нет очень веской, конкретной причины для этого)?
Любые мысли будут оценены. Я могу попытаться опубликовать некоторый код, но я уже пробовал это однажды, и все, казалось, были отвлечены тем фактом, что он не компилируется. Я могу опубликовать соответствующую функцию, но она сама не компилируется.
Вот класс, который вызывает проблему:
// 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;
}
}
}
bad_alloc
возникает из-за проблем с кучей и может быть вызван любым кодом, который косвенно выделяет или освобождает кучу памяти, которая включает в себя все стандартные коллекции библиотек (std::vector
, std::map
и т. д.), а также std::string
,
Если ваши программы не используют много памяти в куче (поэтому они не исчерпывают кучу), bad_alloc
Скорее всего, они вызваны повреждением кучи, которое обычно вызывается использованием висячих указателей в куче.
Вы упоминаете, что ваш код делает много resize
операции — resize
в большинстве коллекций аннулирует все итераторы в коллекции, поэтому, если вы повторно используете любой итератор после resize
что может привести к куче коррупции bad_alloc
исключения. Если вы используете непроверенный доступ к элементам вектора (std::vector::operator[]
), и ваши индексы находятся вне диапазона, что также может привести к повреждению кучи.
Лучший способ отследить повреждение кучи и ошибки памяти в целом — использовать отладчик кучи, такой как Valgrind
Классы как 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).
Если я не использую «new» и «delete», и все мои переменные инициализируются ВНУТРИ локальных функций, возможно ли возникновение проблем утечки / выделения памяти из-за повторных вызовов функций?
Неясно. Если все переменные, на которые вы ссылаетесь, являются локальными, нет. Если вы используете malloc (), calloc () и free (), да.
Есть ли вероятность того, что у меня не хватает памяти стека, если я делаю всю программу через стек?
Нет, если вы получите bad_alloc. Если вы получили ошибку «переполнение стека», да.
Возможно ли мне загрузить некоторые из моих больших таблиц поиска (карты и т. Д.) В кучу, а затем просто использовать стек для моих итераций, где важна скорость?
Что ж, трудно поверить, что вам нужна локальная копия таблицы поиска в каждом кадре стека рекурсивного метода.
Есть ли проблема с использованием изменения размера, связанного с памятью?
Конечно. Вы можете убежать.
Может ли это быть случай, когда я должен использовать «новый» и «удалить»
Сегодня невозможно, не зная больше о ваших структурах данных.
(Во многих случаях меня предупреждали не использовать их, если для этого нет очень веской, конкретной причины)?
Кем? Зачем?
Внутри проблемной функции я создаю переменную класса,
Вы создаете экземпляр класса в стеке. Я думаю. Просьба уточнить.
затем перезаписываю эту переменную около 20 раз (один раз для каждой «итерации» моей модели).
С заданием? Есть ли в классе оператор присваивания? Это правильно? Использует ли сам класс кучную память? Правильно ли он распределяется и удаляется при строительстве, разрушении и назначении?
Поскольку, как вы сказали, вы используете std::vector
с распределителем по умолчанию, проблема возникает, когда вы используете много std::vector::resize(...)
и это происходит после нескольких итераций, я думаю, что вы столкнулись с проблемой фрагментации кучи.