2016-01-03 33 views
1

我正在调试一个复杂的C++应用程序,成千上万的行,许多嵌套的对象(我说这是因为它可能是相关的内存碎片),它是还有OMP/MPI并行(尽管在这里运行单个节点)。内存似乎没有释放后的低级别的分配例程免费

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

我通过调试器运行了valgrind,并在块处理结束时发布了一个详细的snapshop,就在decaching之前和decaching之后。这表明内存消耗堆上会从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-作为堆运行地块,但它确实表明内存没有实际被释放:

-------------------------------------------------------------------------------- 
    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) 
    | 
... 

我敢肯定我们正确地释放了所有的向量(带交换空向量),并没有经典的内存泄漏(即非常一致的使用自动指针等),除此之外,我希望这些显示在香草(即不是页面堆)运行。

任何想法可能会发生什么?什么样的错误只能在页面堆中运行?有可能是内存碎片问题吗?如何解决这个问题?

回答

2

这在具有虚拟内存的系统中很典型。底层的“内存分配”例程(“brk”)实际上只增加了你的地址空间的大小。虚拟内存系统根据进程需要提供实际内存页面,并在需要时为其他进程盗取内存。所以,没有太多理由重新调整内存空间的末端,因为它几乎只是一个数字。

+0

但是在“顶部”不仅虚拟内存增加,驻留内存也增加,而且当这些数字变得太大时,它会崩溃。所以我不担心这个由于学术原因,但非常真实的,因为我不能让我的程序运行到完成... :) –

+1

由于@JSF的建议,我要检查的第一件事是:是你用完了交换空间? –

+0

我不认为真的有这个问题:所描述的问题意味着交换空间不足。这个问题似乎是“为什么”。这是一种奇怪的内存泄漏,还是碎片化,还是仅仅需要比系统更多的内存?我的建议是通过分配更多的资源来处理交换空间不足的问题,无论是在解决“为什么”之前,或者不是。 – JSF

2

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

从您发布的数据中挖掘任何有用的信息非常困难。也许有更好的信息或更好的信息解释,你可以得到更好的区别:

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

2)您的程序泄漏大内存块。有了额外的交换空间,当内核计算出你的程序没有访问哪个页面时,你的程序会稍微减慢一点。

3)您的向量在病理上将分割虚拟地址空间创建为与(2)完全相同的条件,而没有实际的内存泄漏错误。4)你的程序在病态上泄漏了与它仍然访问的内存混合在一起的小块内存,从而产生类似于(1)的条件。

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

我可以狂猜(3)更有可能。但是,与仅仅增加交换空间相比,并不是太多并且专门采取行动将会是很大的努力。

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

0

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

static unsigned long count_of_objects;

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

创建将打印计数器并将其设置为可从调试器调用的函数。

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

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

从您的描述中,我怀疑是内存释放问题。

另一种方法,如其他人提到的,虚拟地址空间增长,但在分配更多内存时不缩小。您可能遇到了虚拟内存问题,但我认为解除分配问题的可能性更大。