среда, ноября 23, 2005

Освобождение памяти, выделенной под vector

Некоторое время назад я писала про выделение памяти под vector и в конце немного затронула тему высвобождения памяти. В большинстве реализаций освободить память, выделенную под vector, можно только с помощью трюка, известного как swap trick. Недавно я вычитала, что в реализации STL из Visual C++ 7.1 память, выделенная под вектор, высвобождается при вызове метода clear(). 7.1 у меня нет, зато у меня есть Microsoft Visual C++ Toolkit 2003. Действительно, освобождается. Для такого кода:

vector <int> v;
v.reserve(17);
cout<<v.capacity()<<endl;
v.clear();
cout<<v.capacity()<<endl;

Вывод получается такой:

Microsoft Visual C++ Toolkit 2003:
17
0 //действительно, освободилась
MSVC++6.0:
17
17
MinGW gcc 3.4.4:
17
17


Я решила копать дальше. Я всегда считала, что метод clear для последовательных контейнеров эквивалентен erase всего контейнера. Нашла упоминание об этом в документации STL на sgi.com. Вот оттуда выдержка:
a.clear() Equivalent to a.erase(a.begin(), a.end())
Запускаю код:
vector <int> v;
v.reserve(17);
cout<<v.capacity()<<endl;
v.erase(v.begin(), v.end());
cout<<v.capacity()<<endl;

Получаю:
Microsoft Visual C++ Toolkit 2003:
17
17 //не освободилась
MSVC++6.0:
17
17
MinGW gcc 3.4.4:
17
17

Получается, что в случае вышеупомянутого тулкита нет обещанной эквивалентности. Так, а что говорит об этом Стандарт? Вот тут интересный момент. Там нет слова "эквивалентно". Там erase(begin(), end()) приписано к clear() в качестве assertion/note для последовательных контейнеров. А вот требование там одно, что post condition: size()==0. Оно тут выполняется.
Так что MSVC++2003 тут прав.
Я пробовала также v.resize(0). Пробовала удалить все элементы вектора с помощью pop_back'ов. Память не освобождается. Это происходит только при вызове clear.

Ссылки по теме:
STL vector and reserve

7 комментариев:

  1. Анонимный28/11/05 16:24

    For doing this portably, you need to write your own allocator... :-(

    ОтветитьУдалить
  2. Анонимный25/3/06 16:53

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

    ОтветитьУдалить
  3. Анонимный12/2/08 23:50

    size() и capacity() - не есть одно и то же. Память изначально выделяется с некоторым запасом, чтобы при добавлении элементов в вектор не было постоянной переаллокации (ибо это тормоза, чем дальше - тем больше). Так что, capacity() говорит о размере пула памяти, выделеной данному вектору, а вот size() - это кол-во элементов, которые реально используются из этого пула.
    Когда capacity() == size(), вставка очередного элемента вызовет
    1) аллокацию нового пула (скорее всего, вдвое большего, чем раньше)
    2) копирование содержимого старого пула в новый
    3) освобождение старого пула
    Если Вам не нравится такое поведение - смотрите на другие контейнеры, например, лист (двусвязный список).

    ОтветитьУдалить
  4. 2Анонимный:
    size() и capacity() - не есть одно и то же.

    Я в курсе :-)

    1) аллокацию нового пула (скорее всего, вдвое большего, чем раньше)

    Вдвое больший пул использовался в старых компиляторах, в VC++6.0, например. Он ведет к довольно неэффективному использованию памяти. В VC++7 уже используется константа 1.5.

    Если Вам не нравится такое поведение

    Речь идет об освобождении памяти, выделенной под вектор, а не о выборе контейнеров...

    ОтветитьУдалить
  5. Анонимный24/7/13 21:06

    Бьерн Страуструп:

    Уменьшение размеров вектора не уменьшает его его емкости. Чтобы вернуть память системе, нужно сделать следующий трюк:

    vector tmp = v;
    v.swap(tmp);

    ОтветитьУдалить
  6. Анонимный23/7/15 19:16

    Ни .clear(), ни reserve(x) у меня память не освобождают.
    Освободить память удаётся только так: std::vector().swap(vec);

    ОтветитьУдалить
  7. Анонимный13/10/16 13:58

    Просит удаления неиспользуемых мощностей.
    Это необязательный просьбой сократить capacity в size. Это зависит от реализации, если запрос выполняется.
    void shrink_to_fit(); // (начиная с C++11)

    Пример:
    std::vector v;
    v.clear(); // очищает содержимое вектора .
    v.shrink_to_fit(); // сокращает зарезервированный размер памяти вектора .

    ОтветитьУдалить