为什么std::map<std::map>不会释放内存?

11
在以下测试程序中,由std::map分配的内存未被释放。总的来说,我们分配了大约2.2GB的内存,虽然我们与一个空容器交换了,但这些内存从未被释放。
当将std::map< std::map > 更改为 std::map< std::vector >时,内存实际上被释放了。
我已经使用valgrind检查了代码,但明显没有发现任何泄漏。
为什么会出现这种情况,我该如何改变这种行为?
#include <cstdlib>
#include <iostream>
#include <vector>
#include <map>
#include <chrono>
#include <thread>

class Test
{
   public:
   std::vector< std::pair< int, int > > myContainer;
   std::map<int,int> myMap;

   Test(){
      for( int i = 0 ; i  < 10000; i++  ){
         std::pair<int, int> pair = std::make_pair<int, int>( rand(), int( i ) );
         //myContainer.push_back( pair );
         myMap.insert( pair );
      }            
   }
};

int main()
{

  std::map<int,Test> myContainer1;

   for( int i = 0 ; i < 5000; i++  ){      
      myContainer1.insert( std::make_pair<int, Test>( rand(), Test() ) ); 
   }      

   std::cout << "ready!" << std::endl;
   std::this_thread::sleep_for( std::chrono::milliseconds( 5000 ) );
   std::cout << "cleaning..." << std::endl;

   {
      std::map<int,Test> tmp;
      myContainer1.swap( tmp );
   }

   std::cout << "cleaning ready!" << std::endl;
   std::this_thread::sleep_for( std::chrono::milliseconds( 15000 ) );

   return 0;
}

3
您如何确定内存没有被释放? - M.M
1
我使用了一个系统监视器,查看了驻留内存大小。当使用 map<vector> 时,我发现进程的 RSS 下降到几 MB。而当使用 map<map> 时,RSS 保持在 2 GB。 - bicu
@bicu 这并不一定意味着内存没有被释放(尽管 RSS 通常 比 VSIZE 更好)。valgrind 通常会报告程序结束时仍有多少内存分配,并且比查看资源监视器统计数据更可靠。你在那里看到了什么值? - Andrew
1
你的评估很可能是错误的!使用哪个操作系统,以及你是如何确定它的?工作集、分页/非分页会有所不同。 - Ajay
1
@Arne 是的(munmap 立即浮现在脑海中)。这可能取决于 stdlib 实现(或底层 libc)如何处理释放。也有可能向量(连续存储)和映射(可能不是)之间的差异会影响内存的清理和分页速度/易用性。 - Andrew
显示剩余3条评论
1个回答

10

std::map内部使用红黑树来存储对象,其中包含许多小对象,但std::vector则连续存储对象,使用一个大的平坦内存块。

glibc以不同方式管理不同类型的内存。

在请求小对象时,通常使用内存池来避免外部碎片,但释放对象时会导致内部碎片,内部碎片永远不会返回系统。

在请求大块时,glibc会分配一个大内存块,其中可能包含外部碎片。但释放后,该内存将返回到系统中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接