Я пытаюсь использовать пользовательский новый обработчик для ответа на ошибки нехватки памяти в программе, где я использую собственный библиотека.
Когда я решаю экземпляр, я кеширую результаты (большой динамический размер вектора), поэтому, если мне нужно решить этот экземпляр снова, у меня уже есть результаты. Через некоторое время может случиться так, что кешируется слишком много экземпляров, поэтому во время вычислений не хватает памяти. В этом случае я бы очистил кеш (или лучше удалил несколько экземпляров).
Самый простой способ, которым я придумал, это иметь глобальную переменную std::map<InputType, ResultType> cache
и установить новый обработчик, который опустошает его (если он уже не пустой, то он возвращается к обработчику по умолчанию). Таким образом, когда распределение завершается неудачно, мой обработчик освобождает часть памяти и возвращает ее, поэтому делается еще одна попытка выделить память.
Однако кажется, что такой обработчик никогда не вызывается, а std::bad_alloc
Исключение всегда выбрасывается.
Я попробовал этот пример взят из Вот. Когда используешь std::vector<int>
пользовательский обработчик вызывается правильно, и выводится «Выделение памяти завершено». Когда используешь Eigen::VectorXi
Я только получил std::bad_alloc
исключение.
#include <Eigen/Dense>
#include <iostream>
#include <vector>
#include <new>
void handler()
{
std::cout << "Memory allocation failed, terminating\n";
std::set_new_handler(nullptr);
}
int main()
{
std::set_new_handler(handler);
try {
while(true) {
const unsigned long int Size = 100000000ul;
// Either one of the following:
// new Eigen::VectorXi(Size);
new std::vector<int>(Size);
}
} catch (const std::bad_alloc & e) {
std::cout << e.what() << '\n';
}
}
Интересно, почему это происходит, возможно, Эйген не использует operator new
выделить память? Я не смог найти ничего в документации. Есть ли способ использовать эту технику с Eigen? Или я должен перехватить исключение на высоком уровне, очистить кеш и снова выполнить вычисления?
Проверьте этот файл в исходном коде Eigen: Eigen / src / Core / util / Memory.h, чтобы увидеть, что Eigen делает в этом отделе. IIRC мы бросаем bad_alloc явно при неудачном размещении. Не стесняйтесь писать в список рассылки, если мы должны что-то изменить.
Благодаря совету Бенуа Джейкоба я отследил ту часть, где Эйген выделяет память. Функция aligned_malloc()
определяется в Eigen/src/Core/util/Memory.h
, Оказалось, он не использует operator new
, но это вызывает malloc()
,
я нашел это пример для реализации operator new
и изменил функцию aligned_malloc()
соответственно:
inline void* aligned_malloc(size_t size)
{
check_that_malloc_is_allowed();
while (true) {
void *result;
#if !EIGEN_ALIGN
result = std::malloc(size);
#elif EIGEN_MALLOC_ALREADY_ALIGNED
result = std::malloc(size);
#elif EIGEN_HAS_POSIX_MEMALIGN
if(posix_memalign(&result, 16, size)) result = 0;
#elif EIGEN_HAS_MM_MALLOC
result = _mm_malloc(size, 16);
#elif (defined _MSC_VER)
result = _aligned_malloc(size, 16);
#else
result = handmade_aligned_malloc(size);
#endif
if(result || !size) {
return result;
}
std::new_handler currentHandler = std::set_new_handler(0);
std::set_new_handler(currentHandler);
if (currentHandler) {
currentHandler();
} else {
throw_std_bad_alloc();
}
}
}
Теперь обработчик памяти корректно вызывается при сбое выделения (есть и другие функции выделения, которые также могут потребоваться изменить, но, похоже, все они обернуты aligned_malloc()
так что проверка в этой функции может охватить все).
Однако на данный момент я не уверен, должен ли Eigen соблюдать новый обработчик: он получает память от malloc()
который не использует исключения, но уведомляет malloc()
неудачи, бросая std::bad_alloc
,