是否需要时间来释放内存?

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

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

例如,在我的主要function的最后

std::cout << "End of execution" << endl; 

但是我的程序的执行将会像这样

$ ./程序
做东西…
执行结束
[也许2分钟的暂停]
$ – 返回到shell

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

我猜这个程序最后是在释放内存。 但是,当您closures应用程序时,使用大量内存的商业应用程序(如photoshop)不会出现这种暂停。

请指教 :)

编辑:最大的数据结构是一个用string键入的unordered_map并存储一个integers list

我在Linux上使用g++ -O2 ,我使用的计算机有128GB的内存(大部分是免费的)。 有几个巨大的物体

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

如果程序结束时数据结构非常复杂,释放它们可能需要很长时间。

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

可以通过肮脏的黑客来简化内存释放 – 至少在那些操作系统上,进程分配的所有内存在进程终止时自动释放。

你可以直接调用libc的exit(3)函数或者操作系统的_exit(2) 。 但是,我会非常小心地核实这不会使C ++析构函数代码可能正在执行的其他(重要)清理操作短路。 而这些做什么或不做什么是高度依赖于系统(操作系统,编译器,libc,你正在使用的API,…)。

是的,释放内存可能需要一些时间,也可能有代码执行像被调用的析构函数。 Photoshop不使用3-8GB的内存。

另外你也许应该添加分析到你的应用程序来确认它是内存的释放,而不是别的。

(我是以ndim的回复开始的,但是很长)

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

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

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

缺点:堆分配对象的析构函数不运行 – 如果你依赖它们(例如保存状态),你就是烤面包。 另外,追踪真实内存泄漏也变得困难得多。

找到原因您应该首先分析正在发生的事情。

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

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

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

当然有可能。

大约7年前,我在一个项目上遇到了类似的问题,但是内存要少得多,但是电脑的速度也是我想的。

最后,我们必须免费看看汇编语言,以便弄清楚它为什么如此缓慢,它似乎基本上将释放的块保存在一个链表中,以便它们可以被重新分配,并且也在扫描该列表寻找块结合。 扫描列表是O(n)操作,但释放“n”个对象将其变为O(n ^ 2)

我们的测试数据花费了大约5秒的时间来释放内存,但是一些客户的数据量是我们每次使用的数据量的10倍左右,并且需要5-10分钟关闭系统上的程序。

我们修正了这个问题,就像刚才终止这个过程所暗示的那样,让操作系统清理了这个混乱(我们知道这对我们的应用程序来说是安全的)。

也许你有一个更合理的免费功能,我们几年前,但我只是想发布,如果你有很多对象免费和O(n)自由操作是完全可能的。

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

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

释放内存可能需要时间 – 数据结构正在更新。 多少时间取决于正在使用的分配器。

此外,可能不仅仅是内存释放正在进行 – 如果正在执行析构函数,可能会比正在执行的更多。

2分钟听起来像很多时间 – 但你可能想要在调试器中逐步清理代码(或者如果更方便的话,使用分析器)来查看实际上一直在使用的内容。

时间可能不会完全被浪费,而是调用所有的析构函数。 你可以提供你自己的分配器,它不调用析构函数(如果地图中的对象不需要被破坏,但只能被释放)。

还要看看另一个问题: 符合C ++ STL的分配器

通常情况下,作为一个进程的内存释放不被视为进程的一部分,而是作为一个操作系统清理功能。 你可以尝试像valgrind的东西,以确保你的记忆正在处理。 然而,编译器也会做一些事情来设置和拆卸你的程序,所以某种性能分析,或者使用调试器来分解在拆解时发生的事情可能是有用的。

当你的程序退出时,调用所有全局对象的析构函数。 如果其中一个需要很长时间,你会看到这种行为。

寻找全局对象并调查它们的析构函数。

对不起,但这是一个可怕的问题。 您需要显示显示您正在使用的特定算法和数据结构的源代码。

这可能是分配,但这只是一个疯狂的猜测。 你的破坏者在做什么? 也许是疯狂的分页。 只是因为你的应用程序分配了X的内存量,这并不意味着它会得到它。 最有可能的是将虚拟内存分页。 根据您的应用程序和操作系统的具体情况,您可能会遇到很多页面错误。

在这种情况下,可能会帮助在后台运行iostat和vmstat以查看发生了什么事情。 如果你看到很多的I / O,这是一个肯定的迹象,你是页面错误。 I / O操作将永远比内存操作更昂贵。

如果最后所有的失败时间纯粹是由于解除分配,我会感到非常惊讶。

运行vmstat和iostat,只要您收到“结束”消息,并寻找I / O进入香蕉的任何迹象。

内存中的对象被组织在一个堆中。 它们不是一次删除,而是逐个删除,删除对象的代价是O(log n)。 释放他们需要loooong。

答案是,是的,这需要很多时间。

你可以通过使用析构函数调用my_object->~my_class()而不是delete my_object来避免free被调用。 您可以通过在类中覆盖和取消operator delete( void * ) {}来避免free所有对象的类。 具有虚拟析构函数的派生类将继承该delete ,否则可以复制粘贴(或者可能using base::operator delete; )。

这比调用exit要干净得多。 只要确定你不需要回忆

我猜你的无序映射是一个全局变量,其构造函数在进程启动时被调用,析构函数在进程退出时被调用。

你怎么知道地图是否有罪?

你可以测试你的unordered_map是否有责任(我想是的),通过分配一个新的,以及,啊哈……忘记删除它。

如果你的流程退出的速度更快,那么你有罪魁祸首。

为什么这是如此slooooooooo?

现在,通过阅读您的文章,为您的无序地图,我看到潜在的分配:

  • 字符串分配缓冲区
  • 列表项(每一个都是一个字符串+其他的东西)
  • 无序的地图项目+桶阵列

如果你在这个无序的地图上有3-8Gb的数据,这意味着上面的每个项目都需要某种新的和删除。 如果你逐一解放所有项目,可能需要时间。

其他原因?

请注意,如果您在流程执行时逐项向地图添加项目, new项目并不完全可察觉…但是当您要清理所有项目时,所有已分配的项目必须同时销毁,这可以解释建设/使用和销毁之间的差异

现在,破坏者可能需要一段时间才能获得额外的理由。

例如,在调试模式下的Visual C ++ 2008中,例如,在销毁STL迭代器时,析构函数验证迭代器是否仍然正确。 这导致我的对象被破坏(这基本上是一个节点树,每个节点都有一个子节点列表,每个地方都有迭代器),导致相当缓慢。

你正在使用gcc,所以也许他们有自己的调试测试,或者你的析构函数正在做额外的工作(例如日志?)…

根据我的经验,免费或删除电话不应占用大量时间。 也就是说,我已经看到很多情况下,因为析构函数做了一些不重要的事情,不得不花费很多时间来破坏对象。 如果在销毁过程中无法确定需要花费什么时间,请使用调试器和/或分析器来确定发生了什么事情。 如果事件探查器显示它确实是调用free()会花费很多时间,那么应该改进内存分配方案,因为您必须创建大量的小对象。

正如你注意到大量的应用程序分配大量的内存,并在关机过程中没有明显的内存,所以没有理由你的程序不能做同样的事情。

如果你确定你没有什么可做的,但是可用的内存(例如,没有文件I / O等等),我会建议(像其他人一样)一个简单的强制进程终止。

问题是当你释放内存的时候,通常它并没有返回到操作系统 – 它被保存在一个被重新分配的列表中,这显然很慢。 但是,如果您终止进程,操作系统将立即回收您的所有内存,这应该快得多。 但是,正如别人所说,如果你有任何需要运行的析构函数,你应该确保它们在强制调用exit()或ExitProcess或者其他函数之前运行。

你应该知道的是,由于缓存效应而释放分配的内存(例如,映射中的两个节点)要比释放向量中的内存慢得多,因为CPU需要访问内存以释放内存并运行任何析构函数。 如果你释放了非常大量的非常多的内存,那么你可能会遇到这种情况,应该考虑改变一些更加连续的结构。

我实际上遇到了一个问题:分配内存比分配内存要快,在分配内存然后解除分配之后,我有内存泄漏。 最终,我发现这就是为什么。