С++ Вектор поймать изменить размер утечки памяти

Я пытаюсь сохранить вектор, полный указателя на объекты Circle. Иногда ловушка bad_alloc работает, но иногда нет, тогда я получаю сообщение об ошибке:

Это приложение запросило у среды выполнения необычное завершение его работы. Пожалуйста, свяжитесь со службой поддержки приложения для получения дополнительной информации».

Возможно, векторный массив не может выделить больше памяти... Но bad_alloc этого не ловит.

Circle *ptr;
vector<Circle*> ptrarray;

try{
  for (long long i = 0; i < 80000000; i++) {
    ptr = new Circle(1,i);
    ptrarray.push_back(ptr);
  }
}catch(bad_alloc &ba){
  cout << "Memory Leak" << endl;
}

Было бы здорово, если бы кто-нибудь мог мне помочь;) Спасибо заранее


person Madeye    schedule 15.05.2014    source источник
comment
пожалуйста, добавьте тег компилятора/ОС. Если система использует ленивое распределение, это объясняет это. Также было бы полезно опубликовать полную программу, чтобы другие могли воспроизвести (и проверить, нет ли ошибок в коде, который вы не показали).   -  person M.M    schedule 15.05.2014
comment
Если вам действительно не хватает памяти, возможно, ресурсов недостаточно даже для запуска вашего обработчика (например, где-то внутри этого cout может быть динамическое выделение, которое может вызвать второй необработанный бросок), в в этом случае программа просто прервет(), и вы получите сообщение, которое вы процитировали.   -  person dlf    schedule 15.05.2014
comment
Вы, вероятно, захотите попробовать ptrarray.reserve(80000000);, прежде чем войти в свой цикл.   -  person Paul R    schedule 15.05.2014
comment
@dlf Есть ли что-нибудь в теле, что нужно создать? Возможно, неявное преобразование в std::string, но я так не думаю. Я думаю, что все статично в отношении области действия обработчика. Было бы довольно странно, если бы не хватило памяти для самого механизма обработки... если бы потребовались какие-то дополнительные.   -  person luk32    schedule 15.05.2014
comment
@ luk32 Я не знаю; ничего в коде, который мы видим, но я не знаю, что делает внутри << или конструктор bad_alloc.   -  person dlf    schedule 15.05.2014
comment
@dlf Ну, даже если ... IMO некоторая память должна быть зарезервирована в начале try, чтобы у такого простого обработчика не было проблем с памятью. Я нахожу это довольно опасным. Даже в случае другого исключения, если вы находитесь на границе OOM, вы можете взорваться из-за чего-то такого простого.   -  person luk32    schedule 15.05.2014
comment
@ luk32 Я не пытаюсь предложить какие-либо изменения; просто указав, что если в процессе обработки первой возникнет вторая ошибка нехватки памяти, это объяснит, почему программа самопроизвольно завершилась без запуска обработчика исключений. Действительно; вы не можете надеяться справиться с условиями нехватки памяти после того, как они произойдут.   -  person dlf    schedule 15.05.2014


Ответы (3)


Многие операционные системы позволяют процессам запрашивать больше виртуальных адресов (номинально доступной памяти), чем у них есть виртуальной памяти для поддержки, при условии, что процессы могут фактически не обращаться ко всем страницам. Известно, что это позволяет разреженным массивам быть практичными в таких системах. Но когда вы обращаетесь к каждой странице, ЦП генерирует прерывание, и ОС должна найти физическую память для резервного копирования этой страницы (переключение на диск / файлы подкачки без ОЗУ и т. Д., Если настроено) - когда все варианты исчерпаны (или иногда когда ваш ОС опасно близка к пределу, и какой-то защитный процесс решает, что лучше убить некоторые процессы, чем допустить, чтобы известные критические начали сбой), вы можете получить ошибку, как вы наблюдали. В конечном счете, на уровне C++ это невозможно контролировать. Вы можете зарезервировать и записать все страницы быстро, поэтому вы, скорее всего, потерпите неудачу до того, как выполните всю свою обработку, но даже в этом случае вы можете быть прерваны из-за отчаянно нехватки памяти.


Отдельно вы можете уместить в памяти намного больше кругов, если будете хранить их по значению. Тем не менее, вы не можете, если sizeof(Circle) > sizeof(Circle*) и фрагментация ограничивают вас, и в этом случае вы можете попробовать std::deque. В любом случае:

try
{
    std::vector<Circle> array;
    array.reserve(80000000);
    for (long long i = 0; i < 80000000; i++) {
        array.emplace_back(1, i);
}
catch (const bad_alloc& ba)
{
    std::cerr << "Memory Exhaustion\n";
}
person Tony Delroy    schedule 15.05.2014
comment
Вау, разве такой подход не обесценивает смысл попытки поймать bad_alloc? Это звучит довольно опасно, потому что вы можете выполнить некоторые операторы, которые будут иметь необратимые последствия и взорвутся позже. Было бы невозможно реализовать что-либо транзакционное. Или я ошибаюсь? - person luk32; 15.05.2014
comment
Такой подход действительно обесценивает концепцию std::bad_alloc. Такова жизнь в современный компьютерный век. Совет Конрада Рудольфа в этом ответе, stackoverflow.com/a/9456758/774499, точен: In вообще вы не можете и не должны пытаться отреагировать на эту ошибку. - person David Hammen; 15.05.2014
comment
Ничего себе, так что в целом нет ничего, что вы могли бы быть уверены в безопасности в случае запуска OOM. Обломки, хорошо, что оперативка дешевая. - person luk32; 15.05.2014
comment
@luk32: оперативная память дешевая, а диск для подкачки намного дешевле, поэтому виртуальной памяти может быть очень много, но если программы обращаются к достаточному количеству выделенной виртуальной памяти, из-за чего они часто блокируются из-за ошибок страниц, тогда вся система запускается. ползти. Лучше всего настроить систему для правильной расстановки приоритетов процессов, например. для Linux читать это - person Tony Delroy; 16.05.2014

Следите за памятью вашего процесса через диспетчер задач — вы можете использовать всю память, разрешенную для процесса (в зависимости от начальной точки и размера Circle).

если вы работаете на машине Win32, то у вас есть ~ 2 ГБ памяти процесса для этой операции.

person NirMH    schedule 15.05.2014
comment
Проблема не в нехватке памяти, а в том, что соответствующее исключение не генерируется и не перехватывается. - person luk32; 15.05.2014

Во-первых, как вы уверены, что единственное возможное исключение — std::bad_alloc? Я настоятельно рекомендую добавить блок catch (...) после блока catch (const bad_alloc&), чтобы убедиться, что вы правы. Конечно, с catch (...) вы не будете знать, что было поймано, только то, что это не было bad_alloc.

Во-вторых, если вы каким-то образом инициируете неопределенное поведение (скажем, путем разыменования указателя NULL), вы не обязательно получите исключение; вы не обязательно получите какое-либо разумное поведение в соответствии с языковыми правилами.

В-третьих, как уже было сказано, в Linux вы можете вызвать убийцу нехватки памяти. Это поведение не совсем соответствует стандартам, но вы можете столкнуться с ним в реальной жизни.

person Max Lybbert    schedule 16.05.2014