Я хотел бы воспользоваться следующей рекламируемой функцией boost::fast_pool_allocator
(увидеть документация Boost для Boost Pool):
Например, у вас может возникнуть ситуация, когда вы хотите выделить
куча мелких предметов в одной точке, а затем достичь точки в вашем
программа, где ни один из них больше не нужен. Использование интерфейсов пула,
вы можете запустить их деструкторы или просто бросить их в
забвение…
(Увидеть Вот за эту цитату.)
Ключевая фраза бросить их в забвение. я не делайте хочу, чтобы деструкторы вызывали эти объекты.
(Причина в том, что у меня есть миллионы крошечных объектов, которые образуют чрезвычайно сложную сеть владения в куче, и моей программе требуется около 20 минут для вызова всех деструкторов, когда единственный родительский объект выходит из стека. I не нужно называть эти деструкторы, потому что нет желаемых побочных эффектов, и вся память содержится в boost::pool
.)
К сожалению, несмотря на обещание вышеуказанной документации и обещание boost::pool
Я не могу найти способ предотвратить вызов деструкторов управляемых объектов.
Проблема легко изолируется в небольшом примере программы:
class Obj
{
public:
~Obj()
{
// Placing a breakpoint here indicates that this is *always* reached
// (except for the crash scenario discussed below)
int m = 0;
}
};
typedef std::map<int, Obj, std::less<int>,
boost::fast_pool_allocator<std::pair<int const, Obj>>>
fast_int_to_int_map;
class Foo
{
public:
~Foo()
{
// When the following line is uncommented, the program CRASHES
// when the destructor is exited - because the Obj destructors
// are called on the invalid Obj ghost instances
//boost::singleton_pool<boost::fast_pool_allocator_tag,
// sizeof(std::pair<int const, Obj>)>::purge_memory();
}
fast_int_to_int_map mmap;
};
void mfoo()
{
// When this function exits, the Foo instance goes off the stack
// and its destructor is called, in turn calling the destructors
// of the Obj instances - this is NOT desired!
Foo foo;
foo.mmap[0] = Obj();
foo.mmap[1] = Obj();
}
int main()
{
mfoo();
// The following line deallocates the memory of the pool just fine -
// but does nothing to prevent the destructors of the Obj instances
// from being called
boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(std::pair<int const, Obj>)>::purge_memory();
}
Как отмечено в комментариях к коду, деструкторы Obj
экземпляры, которые управляются boost::pool
всегда называются.
Что я могу сделать, чтобы сделать многообещающую цитату из документации Boost Pool, drop them off into oblivion
, сбываться?
Вы передаете пользовательский распределитель в свой std::map
учебный класс. Таким образом, этот распределитель будет использоваться для всего внутри std::map
: для всех Obj
данные, а также для узлов двоичного дерева std::map
, В результате, если вы позвоните purge_memory()
в Foo
деструктор, то вся эта память становится недействительной, и она падает в std::map
деструктор.
Ваше предположение, что пользовательский распределитель отвечает за перераспределение объектов, неверно: это std::map
Задача освободить все объекты. Так, ~Obj()
будет вызываться независимо от того, передадите ли вы пользовательский распределитель или используете ли вы по умолчанию.
Я не вижу элегантного решения для вашего вопроса. Но этот подход должен работать:
placement new
создавать Foo
объект из памяти пула,Foo
возражать как обычно,purge_memory()
освободить всю память из пула. Нет деструкторов ~Obj
или же ~std::map
будет называться.По умолчанию пул использует default_user_allocator_new_delete
Распределитель. Это уничтожит лежащие в основе объекты, сначала вызвав деструктор, а затем освободив основную память. default_user_allocator_malloc_free
приведет к тому, что malloc’ed память будет восстановлена без запуска деструктора — следовательно drop[ing] them off into oblivion
,
Тем не менее, если ваше дерево действительно настолько сложное, использование бесплатного вместо запуска деструкторов кажется действительно хорошим способом начать вырубать ветки из-под себя и потенциально начинать утечку памяти, которую вы больше не можете получить.
Ответ на этот вопрос содержится в комментариях под ответом @ qehgt, а также подробно в этот новый вопрос.
А именно:
Существует четкое и формальное различие между двумя взаимосвязанными аспектами создания и удаления объектов:
(1) Распределение и освобождение памяти
(2) Вызов конструкторов и деструкторов
Назначение пользовательского распределителя — только (1).
Контейнерные объекты обрабатывают (2), и они вызывают функции в пользовательском распределителе, чтобы определить расположение памяти, в которой должен создаваться объект, и сообщить пользовательскому распределителю, что все в порядке, чтобы освободить выделенную память для заданных объектов. Но вызовы самого конструктора / деструктора выполняются контейнером, а не пользовательским распределителем.
Поэтому способ достижения цели этого вопроса заключается в следующем: объявите объект контейнера с помощью new
и никогда не звонить delete
на нем (но используйте пользовательский распределитель, чтобы гарантировать, что объекты, которые вы позже создадите в контейнере, будут храниться в пуле памяти, а затем вручную освободить пул памяти, вызвав purge_memory()
):
class Obj { // has operator<() for use with std::set };
typedef std::set<Obj, std::less<Obj>, boost::fast_pool_allocator<Obj>> fast_set_obj;
// Deliberately do not use a managed pointer -
// I will *NOT* delete this object, but instead
// I will manage the memory using the memory pool!!!
fast_set_obj * mset = new fast_set_obj;
// ... add some Obj's to 'mset'
mset->insert(Obj());
mset->insert(Obj());
// Do something desireable with the set ...
...
// All done.
// It's time to release the memory, but do NOT call any Obj destructors.
// The following line of code works exactly as intended.
boost::singleton_pool<boost::fast_pool_allocator_tag, sizeof(Obj const)>::purge_memory();
(Приведенный выше код взят из связанного вопроса выше.)
Тем не менее, у меня все еще есть проблема, не связанная напрямую с намерением, стоящим за этим текущим вопросом: приведенный выше код не работает, если map
используется вместо set
, Увидеть связанный вопрос для деталей.