4

Actually I have millions of vector objects in my program. In default for each vector, the system will assign more space than it actually needed since those vectors are read-only after they are finish loading.

So I want to shrink their capacity to save memory. a typical way is using vector.swap() method as described in this question:

 std::vector<T> tmp(v);    // copy elements into a temporary vector
           v.swap(tmp);              // swap internal vector data

I have tried this code, but found the .swap() operation doesn't reduce the memory cost actually. (I looked at the Private Working Set size in the task manager to get the process's memory usage)

Why is that? UPDATE" I am using VS 2008, and I have added some lines to output the current capacity during runtime. As seen in below code and its output, the vec.capacity did shrink to 10 after swap, but this doesn't reflect in task manager.

Or maybe just like @Adam Rosenfield said, this change didn't return the freed space to OS? Or I shouldn't use the Task Manager to look up its memory usage? I hope this swap() operation can have the effect as same as delete pointer_to_a_big_vec which will directly free the memory.

My test sample is here:

void vecMemoryTest()
{
  vector<int> vec;

  //make it big!
  for(int i = 0; i <= 100000; i++){vec.push_back(i);}
  for(int i = 0; i <= 100000; i++){vec.push_back(i);}

  cout << "Before .resize() : " << vec.capacity() << endl;

  //OK now I only need the first 10 elements
  vec.resize(10);   

  cout << "After .resize() : " << vec.capacity() << endl;

  //So try to free other spaces
  vector<int> tmp(vec.begin(), vec.end());



  vec.swap(tmp);    // now should free wasted capacity
            // But in fact it doesn't

  cout << "After .swap() : " << vec.capacity() << endl;
}

and the output is:

Before .resize() : 207382 After .resize() : 207382 After .swap() : 10

Community
  • 1
  • 1
JXITC
  • 1,110
  • 1
  • 13
  • 27
  • 10
    Even if the memory if properly freed, the runtime library doesn't necessary return the virtual address space back to the OS. – Adam Rosenfield Feb 23 '12 at 19:59
  • 1
    swap trick can be in one line can't it? `vec.swap(vector(vec));` – Mooing Duck Feb 23 '12 at 20:01
  • How are you trying to measure the used capacity. That looks like it should work fine. – Martin York Feb 23 '12 at 20:03
  • @AdamRosenfield I think so too. If this is true, is there any way I can force the process to return this part to OS? Like when I am doing `delete pointer_to_a_big_vec`, this change do reflect in task manager. – JXITC Feb 23 '12 at 20:05
  • 4
    The usual one-liner idiom is `std::vector(v).swap(v);`. – Kerrek SB Feb 23 '12 at 20:14
  • 3
    A related question [How precise is Task Manager](http://stackoverflow.com/questions/3467805/how-precise-is-task-manager) – Bo Persson Feb 23 '12 at 20:22
  • @MooingDuck: no, anonymous objects (annoyingly) cannot be passed as non-`const` references. You could, however, do it the way that Kerrek SB mentioned. – jamesdlin Feb 23 '12 at 20:34

5 Answers5

7

Check the capacity() of the vector. You will most probably find that the vector in fact reduced its memory usage. What you are seeing is probably malloc() implementation not releasing memory back to the OS.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • is there any way to force the process to releasing that part of memory? – JXITC Feb 23 '12 at 20:16
  • and thanks for your reminding I have added some debug information in my sample code, please check the update in my question. :) – JXITC Feb 23 '12 at 20:17
  • 1
    There might be some microsoft-specific api to force libc to trim the heap(s), but do you really need it? Why? – Nikolai Fetissov Feb 23 '12 at 21:28
  • Even though those memory was not released to OS, these part of memory will be reallocated for something else of the process in the future - is this correct? if so, then I don't actually mind this. – JXITC Feb 23 '12 at 22:01
  • 2
    Yes, it's reclaimed by heap manager for future allocations by your process. – Nikolai Fetissov Feb 23 '12 at 23:20
3

The working set counter only takes into account committed virtual address space. It does not take into account how individual users of that address space use it.

A slightly more informative test would be to run vecMemoryTest() multiple times and keep track of the working set. If it does not go up after the first run, you know you are properly freeing memory. If it does go up, it could mean anything.

An even better test would be to modify vecMemoryTest() to allocate a huge vector of the same size again, then destroy it. If the working set stays (mostly) the same, you know you properly shrunk the original vector.

MSN
  • 53,214
  • 7
  • 75
  • 105
1

I haven't looked at the implementation, but it's possible that the copy constructor for vector copies the capacity as well.

Use the iterator form of constructing the new temporary:

vector<int> tmp(vec.begin(), vec.end());
vec.swap(tmp);
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • guaranteed to work, but it may be **very** slow if he has to do it for "millions of vectors" – David Feb 23 '12 at 20:13
1

C++11 provides std::vector::shrink_to_fit, which you may want to try. This function requests that the capacity of the vector be shrunk to fit the size of the data it holds. However, the implementation isn't bound to this request, so you'll have to check how it behaves.

If you want to avoid the 'block' reservations, you'll have to look into a specialized implementation of the standard library; for example, ones used for game development may provide the functionality you need.

It's important to remember that when you request memory with new, the operating system will often give you more memory than you requested. This could have some effect on what you're seeing.

Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71
1

Are you working with Visual Studio 2010? If so it supports C++0x's new method for vector, shrink_to_fit. See MSDN link.

Danra
  • 9,546
  • 5
  • 59
  • 117