почему компилятор откладывает освобождение std :: list? - PullRequest
6 голосов
/ 08 ноября 2011

У меня есть следующий код для проверки освобождения памяти с использованием контейнера std :: list:

#include <iostream>
#include <list>
#include <string>

#include <boost/bind.hpp>

/* count of element to put into container
 */
static const unsigned long SIZE = 50000000;

/* element use for test
 */
class Element
{
public:
    Element()
    : mId(0)
    {}
    Element( long id )
    : mId(id)
    {}
    virtual ~Element()
    {
    }
    inline long getId() const
    {
        return this->mId;
    }

    inline bool operator<( const Element & rightOperand ) const
    {
        return this->mId < rightOperand.mId;
    }

    inline bool isEven() const
    {
        return 0 == ( this->mId & 1 );
    }

private:
    long mId;
};

typedef std::list< Element > Elements;

int main( int argc, char * argv[] )
{
    std::string dummy;
    {
        Elements elements;

        std::cout << "Inserting "<< SIZE << " elements in container" << std::endl;
        std::cout << "Please wait..." << std::endl;

        /* inserting elements
         */
        for( long i=0; i<SIZE; ++i )
        {
            elements.push_back( i );
        }

        std::cout << "Size is " << elements.size() << std::endl;
        std::getline( std::cin, dummy); // waiting user press enter

        /* remove even elements
         */
        elements.remove_if( boost::bind( & Element::isEven, _1 ) );

        std::cout << "Size is " << elements.size() << std::endl;
        std::getline( std::cin, dummy);
    }

    std::getline( std::cin, dummy);

    return 0;
}

Запуск этого кода дает мне следующий профиль памяти:

MemoryProfile

Похоже, что gcc откладывает освобождение и в моей тестовой программе, в конце концов, у него нет выбора и он освобождает память перед возвращением в командную строку.

Почему освобождение происходит так поздно?

Я пытался с вектором протестировать другой контейнер, и приемы сжатия для подгонки работают и освобождают освобожденную память, когда я этого ожидаю.

gcc 4.5.0, linux 2.6.34

Ответы [ 4 ]

8 голосов
/ 08 ноября 2011

Большинство операционных систем (включая Linux) позволяют процессам выделять достаточно большие куски памяти, а не очень маленькие; даже если это возможно, скорее всего, сделать много небольших выделений дороже, чем несколько крупных. Как правило, библиотека C ++ получает большие фрагменты из операционной системы и использует свой собственный менеджер кучи для выделения небольших фрагментов из программы. Большие фрагменты обычно не возвращаются в операционную систему после того, как они были разделены таким образом; они останутся выделенными для процесса и будут использованы повторно для будущих распределений.

list выделяет память небольшими порциями (по одному на узел), и поэтому обычно выделенная память не освобождается до выхода из программы. vector может получить свою память в виде одного большого выделения непосредственно из операционной системы, и в этом случае она будет освобождена после освобождения.

2 голосов
/ 08 ноября 2011

Что именно показывает ваш график?Деструктор std::list освобождает всю память, так что она может быть повторно использована в другом месте программы, но освобождение не обязательно вернет память в систему, где она может использоваться другими процессами.Исторически, по крайней мере, в Unix, когда память была выделена какому-либо процессу, она остается с этим процессом, пока процесс не завершится.Более новые алгоритмы могут реально возвращать память в ОС, но даже тогда такие вещи, как фрагментация, могут помешать этому - если вы выделите, то освободите действительно большой блок, он может быть возвращен, но если вы выделите многомаленькие блоки (что и делает std::list), среда выполнения фактически выделяет большие блоки из ОС, которые она выделяет;такие большие блоки не могут быть возвращены, пока все маленькие блоки в них не будут освобождены, и, вероятно, не будут возвращены даже тогда.

1 голос
/ 08 ноября 2011

Ваш профиль памяти фактически является потреблением адресного пространства процесса (сумма страниц mmap -ed, как, например, дано /proc/self/statm или /proc/self/maps с точки зрения самого процесса).

Но когда функция C или C ++ освобождает память (ранее выделенную с помощью malloc или new, которая использует mmap для получения памяти из ядра Linux), используя free или delete,он не возвращается в систему (с использованием munmap - потому что это будет слишком медленно или непрактично [проблемы фрагментации] - он просто сохраняется как многоразовый для будущего malloc или new.

Таким образом, освобождение произошло по запросу free, но память не возвращена системе, а оставлена ​​для последующего повторного использования.

Если вы действительно хотели вернуть память, напишите свойсобственный распределитель (выше mmap и munmap), но обычно это не стоит усилий.

Возможно, использование Boehm's GC может помочь (это очень полезно, чтобы не беспокоиться о free -ing или delete -ing), если выявно позвоните по номеру GC_gcollect() (но я не уверен в этом), но вас это не должно волновать.

И ваш вопрос технически не связан с gcc (он был бы таким же с другим C ++компилятор).Он связан с malloc и new (то есть со стандартными библиотеками C & C ++) в Linux.

1 голос
/ 08 ноября 2011

Это зависит от того, как вы измеряете использование памяти.Если он измеряет используемую память процесса, это то, что вы могли бы ожидать.

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

Этот способ распределения использовался в прежние времена.Вызов brk или sbrk увеличит размер кучи, предоставив процессу больше памяти.Эта память будет добавлена ​​к арене, с которой были удовлетворены malloc вызовов.

Но free вернет память на арену, необязательно обратно в операционную систему.

Я полагаю, что в этом случае происходит нечто подобное.

...