Вероятно, все столкнулись с этой проблемой, по крайней мере, однажды во время разработки:
while(/*some condition here that somehow never will be false*/)
{
...
yourvector.push_back(new SomeType());
...
}
Поскольку Вы видите, что программа начинает истощать всю системную память, Ваша программа зависает, и Ваша система начинает подкачивать как сумасшедший. Если Вы не распознаете проблему достаточно быстро и уничтожаете процесс, Вы, вероятно, получаете безразличную систему в секундах, где Ваш указатель мыши не делает даже перемещения. Можно или ожидать катастрофический отказ программы с "из памяти" ошибка (который может занять несколько долгих минут), или поразите сброс в компьютер.
Если Вы не можете разыскать ошибку сразу затем, Вам будут нужны несколько тестов и сброса для обнаружения, который является очень раздражающим...
Я ищу возможно межплатформенный способ предотвратить это так или иначе. Лучшим был бы код режима отладки, который выходит из программы, если она выделила слишком много памяти, но как я могу отслеживать, сколько памяти выделяется? Переопределение глобальных новых и операторов delete не поможет, потому что бесплатная функция, которую я вызвал бы в удалении, не даст идеи, сколько байтов освобождено.
Любые идеи ценятся.
Переопределение глобальных операторов new и delete не поможет, потому что бесплатная функция, которую я бы вызвал при удалении, не даст никакого представления о том, сколько байтов освобождено.
Но вы можете это сделать. Вот полная структура для перегрузки операторов глобальной памяти (добавьте ее в какой-нибудь global_memory.cpp
файл):
namespace
{
// utility
std::new_handler get_new_handler(void)
{
std::new_handler handler = std::set_new_handler(0);
std::set_new_handler(handler);
return handler;
}
// custom allocation scheme goes here!
void* allocate(std::size_t pAmount)
{
}
void deallocate(void* pMemory)
{
}
// allocate with throw, properly
void* allocate_throw(std::size_t pAmount)
{
void* result = allocate(pAmount);
while (!result)
{
// call failure handler
std::new_handler handler = get_new_handler();
if (!handler)
{
throw std::bad_alloc();
}
handler();
// try again
result = allocate(pAmount);
}
return result;
}
}
void* operator new(std::size_t pAmount) throw(std::bad_alloc)
{
return allocate_throw(pAmount);
}
void *operator new[](std::size_t pAmount) throw(std::bad_alloc)
{
return allocate_throw(pAmount);
}
void *operator new(std::size_t pAmount, const std::nothrow_t&) throw()
{
return allocate(pAmount);
}
void *operator new[](std::size_t pAmount, const std::nothrow_t&) throw()
{
return allocate(pAmount);
}
void operator delete(void* pMemory) throw()
{
deallocate(pMemory);
}
void operator delete[](void* pMemory) throw()
{
deallocate(pMemory);
}
void operator delete(void* pMemory, const std::nothrow_t&) throw()
{
deallocate(pMemory);
}
void operator delete[](void* pMemory, const std::nothrow_t&) throw()
{
deallocate(pMemory);
}
Затем вы можете сделать что-то вроде:
// custom allocation scheme goes here!
const std::size_t allocation_limit = 1073741824; // 1G
std::size_t totalAllocation = 0;
void* allocate(std::size_t pAmount)
{
// make sure we're within bounds
assert(totalAllocation + pAmount < allocation_limit);
// over allocate to store size
void* mem = std::malloc(pAmount + sizeof(std::size_t));
if (!mem)
return 0;
// track amount, return remainder
totalAllocation += pAmount;
*static_cast<std::size_t*>(mem) = pAmount;
return static_cast<char*>(mem) + sizeof(std::size_t);
}
void deallocate(void* pMemory)
{
// get original block
void* mem = static_cast<char*>(pMemory) - sizeof(std::size_t);
// track amount
std::size_t amount = *static_cast<std::size_t*>(mem);
totalAllocation -= pAmount;
// free
std::free(mem);
}
Если вы работаете в Linux или Unix-ish системе, вы можете проверить setrlimit (2) , который позволяет вам настроить ограничения ресурсов для вашей программы. Вы можете делать аналогичные вещи из оболочки с помощью ulimit .
потому что функция free, которую я бы вызвал в delete, не даст никакого представления о том, сколько байт освобождается
Она может, вам просто придется вести карту размера выделенной памяти по адресу и вычитать нужное количество на основе этой информации во время free.
Вы можете реализовать свой собственный глобальный оператор new:
void* operator new (std::size_t size) throw (std::bad_alloc);
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();
void* operator new (std::size_t size, void* ptr) throw();
void* operator new[] (std::size_t size) throw (std::bad_alloc);
void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_constant) throw();
void* operator new[] (std::size_t size, void* ptr) throw();
Затем просто установите жесткое ограничение на то, сколько памяти вы выделяете; возможно, даже сколько Кб/сек
Если вам нужен простой способ найти все эти потенциальные утечки, просто используйте текстовый редактор и ищите .push_back во всем исходном коде. Затем изучите все случаи вызова этой функции и посмотрите, находятся ли они внутри жесткого цикла. Это может помочь вам найти серьезные проблемы в коде. Конечно, вы можете получить 100 обращений, но это можно проверить за конечный промежуток времени. Или вы можете написать статический анализатор (с использованием Scitools API), чтобы находить все циклы while, в которых есть метод контейнера с именем .push_back, который вызывается внутри них.