解除内存需要时间吗?

jm1*_*890 13 c++ linux memory memory-management

我有一个C++程序,在执行期间,将分配大约3-8Gb的内存来存储哈希表(我使用tr1/unordered_map)和各种其他数据结构.

但是,在执行结束时,返回shell之前会有很长的停顿时间.

例如,在我的主要功能的最后,我有

std::cout << "End of execution" << endl;
Run Code Online (Sandbox Code Playgroud)

但是我的程序执行会像

$ ./program
do stuff ...
执行结束
[可能长达2分钟的停顿]
$ - 返回shell

这是预期的行为还是我做错了什么?

我猜这个程序最终会释放内存.但是,当您关闭应用程序时,使用大量内存的商业应用程序(如photoshop)不会出现此暂停.

请指教 :)

编辑:最大的数据结构是一个unordered_map键入string和存储listintegers.

g++ -O2在linux上使用,我使用的计算机有128GB的内存(其中大部分是免费的).有一些巨大的物体

解决方案:我最终摆脱了哈希表,因为它几乎已经满了.这解决了我的问题.

ndi*_*dim 13

如果程序完成后数据结构足够复杂,那么释放它们实际上可能需要很长时间.

如果你的程序实际上必须创建这样复杂的结构(做一些内存分析以确保),可能没有干净的方法.

可以通过脏黑客缩短内存释放- 至少在进程终止时自动释放进程分配的所有内存的操作系统上.

您可以通过直接调用libc的exit(3)函数或操作系统来实现_exit(2).但是,我会非常小心地验证这不会使某些C++析构函数代码可能正在执行的任何其他(重要)清理短路.这有或没有做的是高度依赖系统(操作系统,编译器,libc,您正在使用的API,......).

  • 调用exit()会杀死你的应用程序会释放它的内存,但它可能无法释放打开的文件句柄,迫使你重启盒子.我见过这种情况发生在Solaris上.而且,假设没有特殊的析构函数是没有意义的.它必须是你的析构者.我几乎可以肯定. (2认同)

Bri*_*ndy 8

是的,内存的重新分配可能需要一些时间,而且可能还有代码执行,就像调用析构函数一样.Photoshop不使用3-8GB内存.

您也应该为应用程序添加分析,以确认它是内存的释放而不是其他内容.


pet*_*hen 7

(我把它作为对ndim的回复开始,但它得到了很长时间)

正如ndim已发布,终止可能需要很长时间.
可能的原因是:

  • 你有很多分配,并且堆的一部分被交换到磁盘.
  • 长期运行的析构函数
  • 其他atexit惯例
  • 特定于操作系统的清理,例如在Windows上通知DLL的线程和进程终止(不知道Linux上究竟发生了什么.)

exit这不是最糟糕的解决方法,但实际行为取决于系统.例如,exit在WIndows/MSVC上,CRT将运行全局析构函数/ atexit例程,然后调用ExitProcess,它会关闭句柄(但不一定要刷新它们 - 至少不能保证).

缺点:堆分配对象的析构函数不会运行 - 如果你依赖它们(例如保存状态),你就是吐司.此外,追踪真正的内存泄漏变得更加困难.

找到原因你应该首先分析发生了什么.

例如,通过手动释放仍分配的根对象,可以将释放时间与其他进程清理分开.记忆是可能的原因与你的描述一致,但它不是唯一可能的.一些清理代码在运行到超时之前也会死锁.监视统计信息(例如CPU /交换活动/磁盘使用)可以提供线索.

检查发布版本 - 调试版本通常使用堆上的额外数据,这会极大地增加清理成本.

不同的分配器
如果分配是问题,您可能会从使用自定义分配机制中受益匪浅.示例:如果您的地图仅增长(项目永远不会删除),竞技场分配器可以提供很多帮助.如果您的整数列表有许多节点,请切换到a vector,或者如果需要随机插入,请使用绳索.


jco*_*der 6

当然有可能.

大约7年前,我在一个项目上遇到了类似的问题,内存要少得多,但我认为计算机速度也慢了.

我们不得不免费查看汇编语言,以找出它为何如此缓慢而且它似乎基本上将释放的块保存在链表中以便可以重新分配它们并且还扫描该列表寻找块结合.扫描列表是一个O(n)操作,但释放'n'对象将其转换为O(n ^ 2)

我们的测试数据大约需要5秒钟来释放内存,但是有些客户的数据大约是我们每次使用的数据的10倍,并且需要5-10分钟来关闭他们系统上的程序.

我们修复了它,正如刚刚终止过程所建议的那样,并让操作系统清理掉混乱(我们知道在我们的应用程序上可以安全地执行).

也许你有几年前我们拥有的更明智的自由功能,但我只是想发布一个完全可能的话,如果你有很多可以释放的对象和一个O(n)自由操作.


Rei*_*ica 5

我无法想象你如何使用足够的内存来解决它,但我加速程序的一种方法是用来boost::object_pool为二叉树分配内存.对我来说主要的好处是我可以把对象池作为树的成员变量,当树超出范围或被删除时,对象池将被一次性删除(让我不必使用)节点的递归解构器).object_pool虽然在退出时调用它的所有对象解构器.我不确定它是否以特殊方式处理空的解构器.

如果你不需要你的allocator来调用一个构造函数,你也可以使用boost::pool,我认为可以更快地解除分配,因为它根本不需要调用解构器而只删除了一块内存free().