V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhiqiang
V2EX  ›  C

C++ delete 对象但内存没被回收?

  •  
  •   zhiqiang · 2018-12-13 12:09:49 +08:00 · 4811 次点击
    这是一个创建于 2200 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这个对象是一个 std::vector<boost::circular_buffer>,我建了几千个这种对象,在 new 时检查内存,大概会增加占用了 10G 内存。

    但随后 delete 这些对象,内存却没有释放。导致程序多来回几次之后占用上白 G 内存然后就挂了。

    不知道有人遇到过同样的案例没?

    系统:ubuntu 16.04, gcc 5.4, boost 1.0.0.68 。

    检查程序实时占用的内存我是看 /proc/self/status 的 VmRSS: 那一行。写了一个函数在 new 和 delete 前后实时获取和显示这个值。

    还有一个奇怪的现象,上面占用内存是在程序多调用了一个模块( dlopen 了一个 so 文件)后才会出现。如果没有那个模块,内存增减都是正常的。而这个模块,里面并未操作这些对象。

    第 1 条附言  ·  2018-12-14 17:50:43 +08:00
    问题已解决。手写了一个内存池,作为 boost::circular_buffer 的 allocator。
    22 条回复    2018-12-14 17:48:47 +08:00
    willm
        1
    willm  
       2018-12-13 12:15:47 +08:00 via Android
    delete 只是标记内存不再使用,并不一定 memset 为 0。你检测是否回收的方法是有问题的
    cepheus
        2
    cepheus  
       2018-12-13 12:17:21 +08:00 via Android
    o 改成智能指针试试什么?
    willm
        3
    willm  
       2018-12-13 12:22:40 +08:00 via Android
    额,看错了。我以为你是直接获取被释放的对象来检测的。

    会不会是这个模块重载了 delete 操作符
    zhiqiang
        4
    zhiqiang  
    OP
       2018-12-13 12:26:31 +08:00
    @willm 重载是不可能的,我没写过这么底层的东西,要么就是 boost 里面的实现有重载。

    我怀疑跟 runtime link 有关系。
    wutiantong
        5
    wutiantong  
       2018-12-13 13:39:36 +08:00
    boost::circular_buffer 的模版参数是什么?
    你 new 的对象是 vector 还是 circular_buffer 还是 circular_buffer 的模版参数?
    zhiqiang
        6
    zhiqiang  
    OP
       2018-12-13 14:19:03 +08:00
    @wutiantong 在扇贝 C++写得多不?

    我上面写的有一定简化。

    原始对象其实是一个 T,里面有两个成员都是 vector of cirular_buffer of int。

    我 new 和 delete 的都是 T。
    GeruzoniAnsasu
        7
    GeruzoniAnsasu  
       2018-12-13 14:26:01 +08:00
    我遇到过加载一个 so 内存泄露的问题,dlopen/close 内存是平衡的,但中途调多一个 api 就不平衡了
    然后开了 asan 也没发现泄露,查了两天最后发现是 so 会自己 mmap 一块内存自己做内存管理,然后为了保证速度在 deallocate 之后还会预留一块内存以便下次分配加速,这时候 dlclose 它并不会自行 munmap,造成的泄露


    我怀疑你遇到的是不是也是 hook 了 allocate/deallocate 造成的问题
    wutiantong
        8
    wutiantong  
       2018-12-13 14:32:44 +08:00
    @zhiqiang 好像就我一个人在写 C++...

    我感觉楼上说得有道理,虽然我不懂这块。。。
    zhiqiang
        9
    zhiqiang  
    OP
       2018-12-13 14:35:46 +08:00
    @GeruzoniAnsasu 我多挂载的 so 也是我写的,里面很简单。唯一有问题的,可能在这个 so 也链接 boost circular_buffer 以及我那个类 T 的实现。

    现在看来可能是 circular_buffer 的 allocate/deallocate 可能有问题。
    arzterk
        10
    arzterk  
       2018-12-13 14:42:28 +08:00
    std::vector<T>里面的 capacity 内存是要手动释放的呀,看过 STL 源码都知道的,参考这个 https://en.cppreference.com/w/cpp/container/vector/shrink_to_fit
    wutiantong
        11
    wutiantong  
       2018-12-13 14:59:08 +08:00
    @arzterk 都已经 delete 了 shrink_to_fit 就不重要了吧。
    wwqgtxx
        12
    wwqgtxx  
       2018-12-13 15:05:43 +08:00
    其实你可以考虑贴出一个可以复现且经过简化的代码放在 gist 上,比这样大家都在猜的效率高得多
    zhiqiang
        13
    zhiqiang  
    OP
       2018-12-13 15:29:58 +08:00
    @wwqgtxx 能简化复现的 bug,不会贴到这里麻烦网友们。
    cxytz01
        14
    cxytz01  
       2018-12-13 15:49:45 +08:00
    vector 使用的 allocator 函数有几种:其中一种是不会释放内存给 OS,只会回收给 STL 内存池,这很霸道;一种是直接 malloc,STL 内部不提供内存池;还有其他 alloctator 函数。
    vector 的模板声明是:template<class T, class Allocator = std::allocator<T>> class vector;

    参考上面,选择适合你的 allocator 函数。另外由于编译器版本不同,默认选择的 allocator 函数也不一样,有的版本默认 allocator 函数选择就是不回收内存的 allocator 函数。

    以上知识点参考侯捷大师:<<c++内存管理>>课程

    年代久远,已经记不清细节。
    codehz
        15
    codehz  
       2018-12-14 00:00:40 +08:00
    请务必检查下加载的模块的 ABI 兼容性,比如说编译器,各种库的版本等等,如果有 ABI 兼容问题,也可能导致出现预期之外的行为...
    zhiqiang
        16
    zhiqiang  
    OP
       2018-12-14 08:47:23 +08:00
    @codehz ABI 兼容性应该没问题。那个 so 也是我自己编译的,同一系统同样环境。

    再说能提示下,怎么检查 ABI 兼容性问题吗?
    zhiqiang
        17
    zhiqiang  
    OP
       2018-12-14 08:58:21 +08:00
    再补充一下我试验的结果:

    我确定 boost::circular_buffer 这个东西的内存实现和常规的不太一样,具体表现在分配的空间如果没有实际用上,那么并不会占用太多的内存。有点类似于按需扩容,但我看 boost 的文档,这东西应该是在初始化一次性分配内存的。

    The circular_buffer only allocates memory when created, when the capacity is adjusted explicitly, or as necessary to accommodate resizing or assign operations.

    但我也没试验出 delete 之后的内存不释放的情况。可能跟上面发现的特性有关。
    zhiqiang
        18
    zhiqiang  
    OP
       2018-12-14 09:30:04 +08:00
    查看了 boost::circular_buffer 的源代码,它用的 allocator 就是 std::vector::allocator,但跟 std::vector 不一样在于,circular_buffer 不会对内存初始化。

    内存没用不占用可能是操作系统搞的,虽然分配了地址,但不用就不分配实际物理地址,导致不会实际占用物理内存。

    delete 不回收内存的原因还是没搞清楚。
    farseeraliens
        19
    farseeraliens  
       2018-12-14 13:07:21 +08:00 via iPhone
    heapprofiler 看内存泄漏。asan 是用来看内存非法访问的。
    zhiqiang
        20
    zhiqiang  
    OP
       2018-12-14 14:19:31 +08:00
    虽然还没搞清楚确切的原因,但很可能跟分配了大量细碎的内存片段有关。我打算把 circular_buffer 都用内存池管理起来。
    wutiantong
        21
    wutiantong  
       2018-12-14 17:28:59 +08:00
    @zhiqiang 就感觉你一直在处理好大一个烂摊子。。。
    zhiqiang
        22
    zhiqiang  
    OP
       2018-12-14 17:48:47 +08:00
    @wutiantong 哈哈,可是这烂摊子都是我写的啊。

    感觉项目一大,各种坑会不断冒出来。见得多就好了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2482 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 03:31 · PVG 11:31 · LAX 19:31 · JFK 22:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.