内存在释放后似乎没有被较低级别的分配例程释放
Memory seemingly not released by lower level allocation routines after free
我正在调试一个复杂的C++应用程序、数万行、许多嵌套对象(我这么说是因为它可能是相关的内存碎片),它也是OMP/MPI并行的(尽管在这里运行单个节点)。
基本循环遍历问题的各个部分,在每个部分都遍历所有相关对象并执行某些操作。这些对象通过可变成员在内部缓存中间结果。最后调用deCache例程,在那里应该清除所有这些中间结果,然后转到下一个块。问题是,在这一步中,内存似乎没有被释放,并且程序在几个块之后就耗尽了内存。
我在调试器中运行valgrind,并在块处理结束时发布了一个详细的snapshop,分别是在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作为堆运行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)
|
...
我非常确信我们正确地释放了所有向量(通过空向量进行交换),并且没有经典的内存泄漏(即非常一致地使用自动指针等),此外,我希望这些在香草(即不是作为堆的页面)运行下显示。
你知道可能发生了什么吗?什么样的错误只在堆运行时显示在页面中?有没有可能是内存碎片问题?如何解决这个问题?
这在具有虚拟内存的系统中很常见。底层的"内存分配"例程("brk")实际上只会增加地址空间的大小。虚拟内存系统在进程需要时提供实际内存页,并在其他进程需要时将其偷回给它们。所以,没有太多理由重新调整你记忆空间的末尾,因为它几乎只是一个数字。
通过向系统添加交换空间而不是修复代码来解决问题可能更容易。
很难从你发布的数据中挖掘出任何有用的信息。也许有了更好的信息或对信息的更好解释,你可以更好地帮助区分:
1) 您的程序正在积极使用超出您理解的内存。如果有额外的交换空间,它的运行速度会慢得多,但至少是完整的。
2) 您的程序正在泄漏大量内存块。有了额外的交换空间,当内核判断出程序没有访问哪些页面时,程序只会稍微慢一点。
3) 您的向量正在病态地分割虚拟地址空间,创建与(2)完全相同的条件,而没有实际的内存泄漏错误。
4) 你的程序病态地泄漏了与它仍然访问的内存混合在一起的小块内存,产生了类似于(1)的条件。
5) 您通过微小的对象分配/释放来管理几乎不可能的碎片组合,以创建与(4)相同的条件。
我可以大胆地猜测(3)更有可能。但幅度不大,与增加交换空间相比,具体针对3采取行动将是一项重大努力。
你可能需要了解一些额外的基础知识。当解除分配时,只有非常大的单个分配才应该从进程返回到操作系统。如果您的内存使用是大量的小到中等分配,那么没有一个分配返回到操作系统是正确的,因此top
永远不应该在内存上看到任何释放。但是,由于您释放了如此多的内存,它应该在进程中很好地整合,并且在程序活动内存使用的下一个高峰期间可以重用,而碎片很少。因此,有一种理论认为,所有这些都会发生:在内存使用的低谷期进行有效的巩固,然后在下一个高峰期有效地重用该内存。你在top
中看到了一些意想不到的东西,不是因为故障,而是因为你的期望是错误的。然后程序失败是因为内存不足,不是因为它未能重用从早期峰值释放的内存,而是因为当前内存使用峰值对于可用内存来说太大了。
我的建议是在类中添加一个静态成员。
静态无符号长count_of_objects;
在构造函数中增加该值。在析构函数中递减。
创建将打印计数器的函数,并对其进行设置,以便可以从调试器中调用它。
这将显示您的对象是否正在被删除。
你可以从一个根类开始,如果有问题,你可以自己解决。
根据你的描述,我怀疑是内存释放问题。
正如其他人所提到的,另一种选择是,当分配更多内存时,虚拟地址空间会增长,但不会收缩。您可能遇到了虚拟内存问题,但我认为更可能是释放问题。
- 释放错误后堆使用
- OpenMP阵列性能较差
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- G锁定铸造到基础上会释放模拟行为
- 在将变量声明为引用时,堆在释放后使用
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 不同/较旧的处理器运行c++代码的方式是否不同
- 为什么当我解模块化时,这个C++代代码"效率较低"?
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 为C++03编译器编写部分unique_ptr,该编译器与较新的编译器在公共代码库上运行
- 正在理解智能指针,但出现错误:未分配正在释放的指针
- C++双重释放或损坏(out)
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 误报警告 PVS 工作室:V821 性能降低。'rhs'变量可以在较低级别的范围内构造
- matlab 内置函数是用某种较低级别的语言编写的吗?
- 在堆栈和堆上使用较低级别的方法获取缓冲区的长度
- 内存在释放后似乎没有被较低级别的分配例程释放
- 较低级别的 std::atomic<unsigned int>
- 处理从较低级对象引发的事件
- 将应用程序对象传递到较低级别的类中