2

I am using unordered_map using g++ 4.9.2 on Solaris 10, but surprisingly I found that clear() does not release heap. Here's the sample code:

#include <iostream>
#include <unordered_map>

int main ()
{
  std::unordered_map<long long, long long> mymap;
  mymap.rehash(200000);
  getchar();
  for (int i = 0; i < 2000000; i++) {
    mymap[i] = i*i;
  }
  std::cout << "current bucket_count: " << mymap.bucket_count() << std::endl;
  std::cout << "current size: " << mymap.size() << std::endl;
  getchar();
  mymap.clear();
  std::cout << "current bucket_count: " << mymap.bucket_count() << std::endl;
  std::cout << "current size: " << mymap.size() << std::endl;
  getchar();
  return 0;
}

I am observing heapsize for the program when the progranm is waiting on getchar(). And, here's teh heap snapshot found through pmap -x <PID> | grep heap

1. While waiting on 1st getchar(): `0002C000     792     792     792       - rwx--    [ heap ]`

2. After 1st getchar(): it prints:

    current bucket_count: 3439651
    current size: 2000000
Heap shows while waiting on 2nd getchar():

    0002C000    3920    3920    3920       - rwx--    [ heap ]
    00400000   73728   72272   72272       - rwx--    [ heap ]

3. After 2nd getchar(): it prints:

    current bucket_count: 3439651
    current size: 0
Heap shows while waiting on 2nd getchar():
0002C000    3920    3920    3920       - rwx--    [ heap ]
00400000   73728   72272   72272       - rwx--    [ heap ]

This shows (step 3) that clear() has no impact on heap. Although, the documentation says,

std::unordered_map::clear
void clear() noexcept;
Clear content
All the elements in the unordered_map container are dropped: their destructors are called, and they are removed from the container, leaving it with a size of 0.

But, my heap count does not reflect that. Is there any other way to release heap occupied by unordered_map object? Or, should I use something else? Please guide how to release memory from unordered_map?

Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69
  • The quote you have from "the documentation" says nothing about freeing memory used internally by the map, only that the map will become empty. Which it clearly is (since you print the size as zero). – Some programmer dude Mar 01 '17 at 10:58
  • 3
    I think you have to understand there is a difference between size and capacity, It wont release the underlying data structure, even if it does release the memory used for the contained elements. When you call clear it probably calls the destructors and set some size variable to 0 but doesnt free the memory held by the container. – Paul Rooney Mar 01 '17 at 11:01
  • Having acquired memory from the Operating System to return in response to `new` calls, the C++ runtime library won't release it until the process ends. – Martin Bonner supports Monica Mar 01 '17 at 11:29

3 Answers3

2

Content of your map is erased. Similarly as files from disk (erased only from index). It wouldn't be effective to free internal memory if you could use it in next time.

If you wont really free map memory you need to destroy whole object. Or you can try to call void reserve( size_type count ); with zero. (I haven't tried it)

elanius
  • 487
  • 2
  • 19
  • reserve(0) might not work because it is non binding and the reserve value would be lesser than the capacity. – Swtsvn Sep 21 '17 at 17:20
2

unordered_map: clear() does not release heap on clear()

The C++ standard does not require clear to release the memory.

But, my heap count does not reflect that.

The documentation mentioned nothing about the heap. The destructors of the elements were called, and as you confirmed, the size is 0. If you add new elements, they may reuse the memory that the previously cleared elements used.

Is there any other way to release heap occupied by unordered_map object?

Destroying the map will definitely release all of its memory.

mymap.rehash(0) might also work.

However, just because memory is freed to the implementation (by a call to free), doesn't mean that the implementation would necessarily release the memory to the operating system. The implementation may decide to reuse the memory for other allocations instead (although, that is typical only for small allocations).

There is no standard way to do this in C++, but Linux provides a function malloc_trim that will attempt to release freed memory from the top of the heap to the OS.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

The memory is not released because the map is supposed to be in continued use until it goes out of scope or is deleted. For this reasonable default use case a re-allocation would cause unnecessary overhead. My suggestions:

  • If it is indeed not needed any longer, limit its scope to where you need it, respectively use smart pointers for the map with the proper scope.
  • If you continue to need it, but not for a while, or only with much fewer elements, swap it with an empty temporary map (which will then be deleted).
  • Call the destructor explicitly and re-create a new map in place.
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62