Larry
Larry

Reputation: 63

Free memory from a long vector

What I wanted to do is to release the memory used by a vector (say vector<vector<int>>) and I used the swap trick, i.e., v.swap(vector<vector<int>>()).

However, what I observed that the swap trick work well for short vectors but NOT for long ones, for example I tried one vector of length 10,000,000, which took 1.4G in memory, after the swap there is still 1.0G not released.

Below is the code I used for testing.

Thanks advance for any idea!

#include <iostream>
#include <vector>

using namespace std;

typedef unsigned long long     int64;

int main()
{
    {
        vector<vector<int64>> batch;
        {
            vector<int64> v;
            for (size_t i = 0; i < 12; ++i)
                v.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch.push_back(v);
        }
        cout << "pause 1" << endl;
        cin.ignore();
        {
            vector<vector<int64>> tmp;
            batch.swap(tmp);
            // I tried several things here.
            //tmp.swap(batch);
            //batch.clear();
            //batch.shrink_to_fit();
            //batch = tmp;
        }
        cout << "pause 2" << endl;
        cin.ignore();
    }
    cout << "pause 3" << endl;
    cin.ignore();
}

Upvotes: 1

Views: 864

Answers (2)

ahans
ahans

Reputation: 1737

James, In silico, and Max all are correct with their remarks. You expect memory to be freed when you call batch.swap(tmp), since tmp is empty. I just ran your program on 64 bit Linux, compiled using gcc 4.6.0. When observing the memory consumption with top, it goes 2 GB -> 1.8 GB -> 1.8 GB. However, when adding a batch2 and filling it after pause 2 just like batch was filled, memory consumption doesn't increase. Running a version where batch.swap(tmp) is commented out, thus actually having two huge vectors, the memory consumption goes 2 GB -> 3.2 GB -> 2.8 GB -> 2.8 GB.

Here's the complete code with command line option added:

#include <iostream>
#include <vector>
#include <string.h>

using namespace std;

typedef unsigned long long     int64;

int main(int argc, char** argv)
{
    bool no_swap = (argc > 1 && strcmp(argv[1], "-noswap") == 0); 
    {
        vector<vector<int64> > batch;
        vector<vector<int64> > batch2;
        {
            vector<int64> v;
            for (size_t i = 0; i < 12; ++i)
                v.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch.push_back(v);
        }
        cout << "pause 1" << endl;
        cin.ignore();
        {
            vector<vector<int64> > tmp;
            if (no_swap) {
                cout << "NOT calling batch.swap(tmp)" << endl;
            } else {                    
                cout << "calling batch.swap(tmp)" << endl;
                batch.swap(tmp);
            }
        }
        cout << "pause 2" << endl;
        cin.ignore();
        {
            vector<int64> v2;
            for (size_t i = 0; i < 12; ++i)
                v2.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch2.push_back(v2);
        }
        cout << "pause 3" << endl;
        cin.ignore();
    }
    cout << "pause 4" << endl;
    cin.ignore();
}

Some explanation of this behavior can be found here and here. From the latter:

In use, allocator may allocate and deallocate using implementation-specified strategies and heuristics. Because of this, every call to an allocator object's allocate member function may not actually call the global operator new. This situation is also duplicated for calls to the deallocate member function.

According to this document, caching can be disabled by setting GLIBCXX_FORCE_NEW, but this doesn't work for me ...

Upvotes: 1

Puppy
Puppy

Reputation: 146940

What's likely is that your CRT implementation decided that it really would be a better idea not to give that memory back to the operating system just yet in case you want another 12 80MB contiguous allocations.

Upvotes: 1

Related Questions