在免费之后,内存似乎不会被较低级别的分配例程释放

时间:2016-01-03 12:38:29

标签: c++ memory-management memory-leaks valgrind massif

我正在调试一个复杂的C ++应用程序,成千上万行,许多嵌套对象(我之所以这样说是因为它可能与内存碎片相关),它也是OMP / MPI并行化(尽管这里运行单个节点) )。

基本循环遍历问题的大块,在每个块上它遍历所有相关对象并执行某些操作。这些对象通过可变成员在内部缓存中间结果。最后调用deCache例程,清除所有这些中间结果,然后转到下一个块。问题是在这一步中似乎没有释放内存,并且程序在几个块之后耗尽内存。

我通过调试器运行valgrind并在块处理结束时发布了一个详细的snapshop,就在decach之前和decade之后。这表明堆上的内存消耗从23Gb到820Mb,正如预期的那样:

  --------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  0 12,019,170,891,847   23,406,329,728   23,015,422,037   390,907,691            0
98.33% (23,015,422,037B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->44.49% (10,414,094,336B) 0x771D63: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
    | ->37.49% (8,774,281,216B) 0x5B6F4E: FTCinvdCdpZ::clone() const (stl_construct.h:75
...

掉线

    -----------------------------------------------------------------------------
          n        time(i)         total(B)   useful-heap(B) extra-heap(B)        stacks(B)
    --------------------------------------------------------------------------------
      0 12,020,946,295,906      857,944,344      830,426,901    27,517,443            0
    96.79% (830,426,901B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
    ->21.15% (181,458,432B) 0x712267: void std::vector<GTHSpecSampFunc, std::allocator<GTHSpecSampFunc> >::_M_emplace_back_aux<GTHSpecSampFunc>(GTHSpecSampFunc&&) (new_allocator.h:104)
    ...

这些数字正是我所期望的。问题是顶部显示的内存几乎没有减少(实际上它会在一段时间后耗尽内存)。使用--stacks-as-heap运行massif,它确实表明内存实际上没有释放:

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  0 12,286,840,539,442   24,112,730,112   24,112,730,112             0            0
100.00% (24,112,730,112B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
->99.54% (24,000,663,552B) 0x84392D9: mmap (in /lib64/libc-2.12.so)
| ->54.83% (13,220,446,208B) 0x83CB2DF: new_heap (in /lib64/libc-2.12.so)
| | ->53.44% (12,884,901,888B) 0x83CDB19: _int_malloc (in /lib64/libc-2.12.so)
| | | ->53.44% (12,884,901,888B) 0x83CE6AF: malloc (in /lib64/libc-2.12.so)
| | |   ->53.44% (12,884,901,888B) 0x7C74806: operator new(unsigned long) (new_op.cc:49)
| | |     ->28.94% (6,979,321,856B) 0x771D13: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
...

几乎没有变成

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
      0 12,292,664,324,363   23,777,185,792   23,777,185,792             0            0
    100.00% (23,777,185,792B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
    ->99.53% (23,665,119,232B) 0x84392D9: mmap (in /lib64/libc-2.12.so)
    | ->54.47% (12,952,010,752B) 0x83CB2DF: new_heap (in /lib64/libc-2.12.so)
    | | ->53.06% (12,616,466,432B) 0x83CDB19: _int_malloc (in /lib64/libc-2.12.so)
    | | | ->53.06% (12,616,466,432B) 0x83CE6AF: malloc (in /lib64/libc-2.12.so)
    | | |   ->53.06% (12,616,466,432B) 0x7C74806: operator new(unsigned long) (new_op.cc:49)
    | | |     ->28.22% (6,710,886,400B) 0x771D13: FTCinvdCdp::FTCinvdCdp(FTCinvdCdp const&) (new_allocator.h:104)
    | | |     | ->24.84% (5,905,580,032B) 0x5B6EFE: FTCinvdCdpZ::clone() const (stl_construct.h:75)
    | 
...

我很确定我们正确地释放所有向量(使用空向量交换)并且没有经典的内存泄漏(即非常一致地使用自动指针等),此外我希望这些可以在vanilla下显示(即不是page-as-heap)运行。

对可能发生的事情有所了解?什么样的错误只会在页面堆中运行?它有可能是内存碎片问题吗?如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

这在具有虚拟内存的系统中很常见。基础&#34;内存分配&#34;例程(&#34; brk&#34;)实际上只是增加了地址空间的大小。虚拟内存系统在您的进程需要时提供实际内存页面,并在需要时将其窃取回其他进程。因此,没有太多理由重新调整内存空间的结束,因为它只是一个数字。

答案 1 :(得分:2)

通过向系统添加交换空间而不是修复代码,可能更容易解决问题。

很难从您发布的数据中挖掘出任何有用的信息。也许通过更好的信息或更好的信息解释,您可以更好地帮助区分:

1)您的程序正在积极使用比您理解的更多内存。有了额外的交换空间,它将运行得慢得多,但至少是完整的。

2)你的程序正在泄漏大内存块。有了额外的交换空间,你的程序只会慢一点,因为内核会找出你的程序仍然无法访问的页面。

3)您的向量在病理上将虚拟地址空间分段,创建与(2)完全相同的条件而没有实际的内存泄漏错误。

4)你的程序在病理上泄漏了与它仍然访问的内存混合的微小内存块,创建了类似于(1)的条件。

5)你通过微小的对象分配/释放来管理几乎不可能的碎片混合,以创建与(4)相同的条件。

我可能会猜测(3)更有可能。但是,与仅仅增加交换空间相比,并没有那么多,并且特别注重3将是一项重大的工作。

您可能需要了解的一些额外基础知识。在解除分配时,应该只将非常大的单个分配从进程返回到OS。如果您的内存使用是大量的小到中等分配,那么将它们都没有返回到操作系统是正确的,因此top永远不会在内存上看到任何释放。但是,由于你释放了如此多的内存,应该在流程中很好地整合,并且可以在程序的活动内存使用的下一个峰值期间以非常小的碎片重用。因此,有一种理论认为,所有这一切都会发生:在内存使用的谷中有效整合,然后在下一个峰值中有效地重用该内存。您在top中看到了一些意想不到的东西,不是因为故障而是因为您的期望是错误的。然后程序由于内存不足而失败,不是因为它无法重用从早期峰值释放的内存,而是因为内存使用中的当前峰值对于可用内存来说太大了。

答案 2 :(得分:0)

我的建议是在你的课程中添加一个静态成员。

static unsigned long count_of_objects;

增加构造函数中的值。在你的析构函数中减少它。

创建将打印计数器并进行设置的功能,以便可以从调试器中调用它。

这将显示您的对象实际上是否被删除。

如果出现问题,您可以从根类开始,然后继续努力。

根据您的描述,我怀疑存在内存释放问题。

正如其他人所提到的那样,替代方案是虚拟地址空间增长但在分配更多内存时不会缩小。您可能遇到了虚拟内存问题,但我认为更有可能出现解除分配问题。