使用std map和shared_ptr的奇怪内存行为

麦克风

下面的代码在我的Debian机器上引起了奇怪的内存行为。即使清除了映射后,htop仍显示该程序仍使用大量内存,这使我认为存在内存泄漏。奇怪的事实是它仅在某些情况下出现。

#include <map>
#include <iostream>
#include <memory>


int main(int argc, char** argv)
{
    if (argc != 2)
    {
        std::cout << "Usage: " << argv[0] << " <1|0> " << std::endl;
        std::cout << "1 to insert in the second map and see the problem "
                "and 0 to not insert" << std::endl;
        return 0;
    }

    bool insertion = atoi(argv[1]);
    std::map<uint64_t, std::shared_ptr<std::string> > mapStd;
    std::map<uint64_t, size_t> counterToSize;
    size_t dataSize = 1024*1024;
    uint64_t counter = 0;

    while(counter < 10000)
    {
        std::shared_ptr<std::string> stringPtr =
                std::make_shared<std::string>(dataSize, 'a');
        mapStd[counter] = stringPtr;

        if (insertion)
        {
            counterToSize[counter] = dataSize;
        }
        if (counter > 500)
        {
            mapStd.erase(mapStd.begin());
        }
        std::cout << "\rInserted chunk " << counter << std::flush;

        counter++;
    }

    std::cout << std::endl << "Press ENTER to delete the maps" << std::endl;
    char a;
    std::cin.get(a); // wait for ENTER to be pressed

    mapStd.clear();   // clear both maps
    counterToSize.clear();

    std::cout << "Press ENTER to exit the program" << std::endl;

    std::cin.get(a); // wait for ENTER to be pressed
    return 0;
}

解释:

该代码在堆栈上创建了两个映射(但是如果在堆上创建,则问题是相同的)。然后,它将字符串的std :: shared_ptr插入第一张地图。每个字符串的大小为1MB。插入500个字符串后,每个新插入的字符串都会删除第一个,因此映射使用的总内存始终等于500MB。当总共插入了10000个字符串时,程序将等待用户按ENTER键。如果启动程序并将1作为第一个参数传递,则对于第一个映射中的每个插入,还将对第二个映射进行另一个插入。如果第一个参数为0,则不使用第二个映射。首次按下ENTER键后,两个地图都将被清除。该程序仍在运行,并再次等待按下ENTER键,然后退出。

这是事实:

  • 在我的64位Debian 3.2.54-2上,按ENTER键(从而清除了映射后),并且在程序以第一个参数1启动(因此在第二个映射中插入)的情况下,htop指示程序仍然使用500MB的内存!如果使用第一个参数0来启动程序,则将正确释放内存。

  • 本机使用g ++ 4.7.2和libstdc ++。so.6.0.17。我已经尝试过g ++ 4.8.2和libstdc ++。so.6.0.18,同样的问题。

  • 我试过了g ++ 4.9.2和libstdc ++。so.6.0.20的64位Fedora 21,同样的问题。
  • 我已经尝试过使用g ++ 4.8.2和libstdc ++。so.6.0.1932位Ubuntu 14.04,但不会出现问题!
  • 我已经尝试过使用g ++ 4.7.2和libstdc ++。so.6.0.1732位Debian 3.2.54-2,但问题没有出现!
  • 我在64位Windows上尝试过,但没有出现问题!
  • 在存在问题的计算机上,如果您反转清除映射的行(因此,如果先清除uint64_t,size_t映射,则问题将消失!

有人对此有一个解释吗?

查理

我建议先看这里,然后再看这里基本上,libc malloc首先使用mmap进行“大”分配(> 128k),使用brk / freelists进行小分配。一旦释放了一个较大的分配,它就会尝试在可能使用malloc的位置调整大小,但前提是该大小小于最大值(在第一个链接中定义)。在32位的情况下,您的字符串远高于最大值,因此它继续对大型分配使用mmap / munmap,并且仅将较小的映射节点分配放入使用sbrk从系统检索的内存中。这就是为什么您在32位系统上看不到“问题”的原因。

另一位是碎片之一,空闲时尝试合并内存并将其返回给系统。默认情况下,free会将小块粘贴到空闲列表上,以便它们准备下一个请求。如果在堆的顶部释放了足够大的块,它将尝试将内存返回给系统,请参见此处的注释阈值为64K。

如果您通过了分配模式,则分配模式1可能会使counterToSize映射的某些元素靠近堆的顶部,从而阻止该值在字符串的最后一次释放时被释放。counterToSize地图内部各种对象的释放量不足以触发阈值。

如果切换.clear()呼叫顺序,您会发现内存已按预期释放。另外,如果您要分配大量内存并在清除后立即释放它,则会触发释放。(在这种情况下,大只需要超过128个字节-用于触发快速垃圾箱的最大大小。(即,具有该大小的free且分配的空间小于该大小的free进入列表。

我希望那是清楚的。基本上,这不是一个真正的问题。您已经映射了一些页面。您没有在它们上使用任何东西,但是可能释放它们的最后一个免费软件太小,无法触发该代码路径。下次尝试分配某些内容时,它将从您已有的内存中提取(您可以在不增加内存的情况下再次执行整个循环)。

哦,malloc_trim()如果您当时确实需要返回页面,则可以手动调用并强制它进行合并/清除。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用std map和shared_ptr的奇怪内存行为

来自分类Dev

std::shared_ptr(s) 和内存泄漏

来自分类Dev

使用std shared_ptr作为std :: map键

来自分类Dev

如何使用shared_ptr和SWIG避免内存泄漏

来自分类Dev

std :: unique_ptr和std :: shared_ptr之间的破坏行为差异的原理是什么?

来自分类Dev

std :: shared_ptr预分配内存

来自分类Dev

使用std :: move与std :: shared_ptr

来自分类Dev

使用std :: shared_ptr和指向成员函数的指针

来自分类Dev

使用shared_ptr和make_shared时出现奇怪的编译器错误

来自分类Dev

std :: shared_ptr和继承

来自分类Dev

std :: function和shared_ptr

来自分类Dev

std :: shared_ptr和dlopen(),避免未定义的行为

来自分类Dev

在信号处理程序中使用`std :: shared_ptr`和`std :: weak_ptr`是否安全?

来自分类Dev

使用* this作为std :: shared_ptr

来自分类Dev

使用* this作为std :: shared_ptr

来自分类Dev

使用std :: shared_ptr的std :: map调用const重载函数

来自分类Dev

使用braced-init初始化std :: shared_ptr <std :: map <>>

来自分类Dev

使用XCode / Clang用std :: shared_ptr实例化std :: map时出错

来自分类Dev

使用std :: shared_ptr和std :: thread的编译器错误

来自分类Dev

使用std :: shared_ptr和std :: thread的编译器错误

来自分类Dev

使用模板将std :: shared_ptr <Derived>上载到std :: shared_ptr <Base>

来自分类Dev

std :: enable_if和std :: shared_ptr

来自分类Dev

std :: unordered_map插入错误shared_ptr C ++

来自分类Dev

std :: unordered_map插入错误shared_ptr C ++

来自分类Dev

std :: unique_ptr和std :: ostringstream(SIGSEGV)的奇怪行为

来自分类Dev

使用shared_ptr时发生内存泄漏

来自分类Dev

使用相同的函数但具有不同的重载来转换std :: tr1 :: shared_ptr <T>和std :: shared_ptr <T>

来自分类Dev

通过 std::shared_ptr 使用 Rcpp 和 RcppParallel 的线程安全函数指针

来自分类Dev

QVariant与std :: shared_ptr

Related 相关文章

  1. 1

    使用std map和shared_ptr的奇怪内存行为

  2. 2

    std::shared_ptr(s) 和内存泄漏

  3. 3

    使用std shared_ptr作为std :: map键

  4. 4

    如何使用shared_ptr和SWIG避免内存泄漏

  5. 5

    std :: unique_ptr和std :: shared_ptr之间的破坏行为差异的原理是什么?

  6. 6

    std :: shared_ptr预分配内存

  7. 7

    使用std :: move与std :: shared_ptr

  8. 8

    使用std :: shared_ptr和指向成员函数的指针

  9. 9

    使用shared_ptr和make_shared时出现奇怪的编译器错误

  10. 10

    std :: shared_ptr和继承

  11. 11

    std :: function和shared_ptr

  12. 12

    std :: shared_ptr和dlopen(),避免未定义的行为

  13. 13

    在信号处理程序中使用`std :: shared_ptr`和`std :: weak_ptr`是否安全?

  14. 14

    使用* this作为std :: shared_ptr

  15. 15

    使用* this作为std :: shared_ptr

  16. 16

    使用std :: shared_ptr的std :: map调用const重载函数

  17. 17

    使用braced-init初始化std :: shared_ptr <std :: map <>>

  18. 18

    使用XCode / Clang用std :: shared_ptr实例化std :: map时出错

  19. 19

    使用std :: shared_ptr和std :: thread的编译器错误

  20. 20

    使用std :: shared_ptr和std :: thread的编译器错误

  21. 21

    使用模板将std :: shared_ptr <Derived>上载到std :: shared_ptr <Base>

  22. 22

    std :: enable_if和std :: shared_ptr

  23. 23

    std :: unordered_map插入错误shared_ptr C ++

  24. 24

    std :: unordered_map插入错误shared_ptr C ++

  25. 25

    std :: unique_ptr和std :: ostringstream(SIGSEGV)的奇怪行为

  26. 26

    使用shared_ptr时发生内存泄漏

  27. 27

    使用相同的函数但具有不同的重载来转换std :: tr1 :: shared_ptr <T>和std :: shared_ptr <T>

  28. 28

    通过 std::shared_ptr 使用 Rcpp 和 RcppParallel 的线程安全函数指针

  29. 29

    QVariant与std :: shared_ptr

热门标签

归档