![]() |
1
332
假设您有一个“大”(32字节)的可用内存:
现在,分配一些(5个分配):
现在,试着分配16个字节。哎呀,我不能,尽管有将近两倍的免费。 在具有虚拟内存的系统上,碎片问题比您想象的要小,因为大型分配只需要在内存中连续 事实上的 地址空间,不在 身体的 地址空间。所以在我的例子中,如果我有一个页面大小为2字节的虚拟内存,那么我可以毫无问题地进行16字节的分配。物理内存如下所示:
而虚拟内存(要大得多)可能是这样的:
内存碎片的典型症状是,你试图分配一个大的块,但你不能,即使你似乎有足够的内存可用。另一个可能的后果是进程无法将内存释放回操作系统(例如,因为它从操作系统分配的每个大块)
在C++工作中通过根据它们的大小和/或它们的预期寿命分配对象来防止内存碎片化的策略。因此,如果要创建大量对象并在以后一起销毁它们,请从内存池中分配它们。在它们之间执行的任何其他分配都不会来自池,因此不会位于它们之间的内存中,因此不会导致内存碎片化。或者,如果要分配许多相同大小的对象,则从同一个池中分配它们。那么,池中的一段可用空间永远不能小于您试图从该池中分配的大小。
标准库并不比其他分配内存的库差,标准容器都有一个
|
![]() |
2
86
内存碎片是指当你的大部分内存被分配到大量不连续的块或块中时,你的总内存中有很大一部分没有被分配,但在大多数典型的情况下是不可用的。这会导致内存不足异常或分配错误(即malloc返回null)。 最简单的思考方法是想象你有一堵空墙,你需要把照片放进去 大小不一的 打开。每张照片都有一定的尺寸,很明显你不能把它分成更小的几块,以使它合身。你需要在墙上有一个空的地方,图片的大小,否则你就放不起来。现在,如果你开始把图片挂在墙上,而你对如何排列它们不小心,你很快就会发现墙上部分地挂满了图片,即使你可能有空的地方,大多数新图片也放不下,因为它们比可用的地方大。你仍然可以挂很小的照片,但大多数都不适合。因此,你必须重新安排(压缩)那些已经在墙上腾出更多的空间。。
如何判断内存碎片是否是我的应用程序的问题?什么样的项目最有可能受到影响? 一个迹象表明,你可能正在处理内存碎片是如果你得到许多分配错误,特别是当使用内存的百分比很高-但不是你还没有用完所有的内存-所以从技术上讲,你应该有足够的空间,你试图分配的对象。 当内存严重碎片化时,内存分配可能需要更长的时间,因为内存分配器必须做更多的工作才能为新对象找到合适的空间。如果反过来又有许多内存分配(可能是由于内存碎片导致的),分配时间甚至可能会导致明显的延迟。 处理内存碎片的常用方法有哪些? 使用一个好的算法来分配内存。与其为许多小对象分配内存,不如为这些小对象的连续数组预分配内存。有时,在分配内存时有点浪费可以提高性能,还可以省去处理内存碎片的麻烦。 |
![]() |
3
24
内存碎片和磁盘碎片的概念是一样的:它指的是由于使用中的区域没有足够紧密地组合在一起而浪费的空间。 假设一个简单的玩具示例有10个字节的内存:
现在让我们分配三个三字节块,分别命名为A、B和C:
现在释放块B:
现在,如果我们尝试分配一个四字节的数据块D,会发生什么?嗯,我们有四个字节的空闲内存,但我们没有四个字节 相邻的 字节的内存可用,所以我们不能分配D!这是对内存的低效使用,因为我们本应该能够存储D,但却无法。我们不能移动C来腾出空间,因为我们的程序中很可能有一些变量指向C,我们不能自动找到并更改所有这些值。 你怎么知道这是个问题?最大的迹象是你的程序的虚拟内存比你实际使用的内存要大得多。在一个现实世界的例子中,你会有超过10个字节的内存,所以D将从字节9开始分配,而字节3-5将保持未使用状态,除非你以后分配了3个字节或更小的内存。 在这个例子中,3个字节并不是一大堆浪费,但是考虑一个更病态的情况,在这个情况下,两个字节的分配在内存中相隔10兆字节,并且需要分配一个大小为10兆字节+1字节的块。你必须要求操作系统多提供超过10兆字节的虚拟内存才能做到这一点,尽管你已经离拥有足够的空间只有一个字节了。
一般来说,分配的次数越少,内存碎片化的可能性就越小。然而,STL相当有效地处理了这个问题。如果您有一个字符串正在使用其当前分配的全部内容,并且您在其中附加了一个字符,那么它不会简单地重新分配到其当前长度加上一个字符,而是 双打 尽管STL容器当然不会共享内存 之间 |
![]() |
4
14
内存碎片是指内存变得不可用的问题,即使它在理论上是可用的。有两种碎片: 是已分配但无法使用的内存(例如,当内存分配为8字节块,但程序仅需要4字节时重复执行单个分配)。 问题是空闲内存被分成许多小块,这样虽然有足够的总空闲内存,但无法满足大型分配请求。
如果您的程序使用的系统内存比实际paylod数据需要的内存多得多,那么内存碎片就是一个问题(您已经排除了内存泄漏的可能性)。 使用一个好的内存分配器。IIRC,那些使用“最佳匹配”策略的人在避免碎片化方面通常要高得多,如果慢一点的话。然而,研究也表明,对于任何分配策略,都存在病态的最坏情况。幸运的是,对于分配器来说,大多数应用程序的典型分配模式实际上是相对良性的。如果你对细节感兴趣的话,这里有一堆文件:
|
![]() |
5
9
更新:
我一直在开发一个服务器应用程序,它在HP-UX 11.23/11.31 ia64上存在内存碎片问题。 它看起来像这样。有一个进程进行了内存分配和释放,并运行了数天。即使没有内存泄漏,进程的内存消耗也在不断增加。
关于我的经历。在HP-UX上,使用HP-UX gdb很容易找到内存碎片。设置一个断点,点击后运行以下命令:
我改善现状的方法就是这样。在我用HP-UX gdb做了一些分析之后,我发现内存问题是由我使用
我的理解是,在STL密集型应用程序中,必须首先确定问题。内存分配器(如libc中的)实际上处理大量小分配的问题,这对于
|
![]() |
6
6
内存碎片最有可能发生在分配和 许多大小不一的物体。假设内存中有以下布局:
现在,什么时候
ring buffers
和
object pools
. 在STL的上下文中,方法
|
![]() |
7
6
关于内存碎片的一个非常详细的答案可以在这里找到。 http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/ 这是我11年来为人们提供的关于记忆碎片问题的答案的总结软件验证.com |
![]() |
8
3
支离破碎的
另一个不可避免但问题较少的碎片来源是,在大多数体系结构中,内存地址必须
显而易见的答案是,你得到了一个内存不足的异常。 显然,没有一种好的便携方式来检测C++应用程序中的内存碎片。看到了吗 this answer 更多细节。
|
![]() |
9
3
这是一个超级简化版的假人。
这就是所谓的分裂。 |
![]() |
10
2
当您想在堆中添加一个项目时,所发生的是计算机必须搜索空间来容纳该项目。这就是为什么不在内存池或池分配器上执行动态分配时会“减慢”速度。对于一个繁重的STL应用程序,如果您正在执行多线程,那么 Hoard allocator 或者 TBB Intel 现在,当记忆支离破碎时,会发生两件事:
|
![]() |
11
1
发生内存碎片是因为请求了不同大小的内存块。考虑100字节的缓冲区。您需要两个字符,然后是一个整数。现在释放这两个字符,然后请求一个新的整数-但是这个整数不能放在这两个字符的空间中。该内存不能被重用,因为它不在一个足够大的连续块中,无法重新分配。除此之外,还为char调用了大量分配器开销。 基本上,在大多数系统中,内存只以一定大小的块出现。一旦将这些块拆分,在释放整个块之前,它们不能重新连接。这可能导致整个块在使用,而实际上只有一小部分块在使用。
您应该记住的是,在32位x86桌面系统上,您拥有整个2GB的内存,这些内存被拆分为4KB的“页面”(非常确定所有x86系统上的页面大小都是相同的)。您必须调用一些omgwtfbbq片段才能出现问题。碎片化确实是一个过去的问题,因为现代堆对于绝大多数应用程序来说都过大,而且有很多系统能够承受它,比如托管堆。 |
![]() |
12
0
“元素:魔法之战” ,一款电脑游戏 Stardock 这款游戏是为32位/2GB内存设计的,必须对内存管理进行大量优化,才能使游戏在2GB内存中运行。由于“优化”会导致不断的分配和取消分配,随着时间的推移,堆内存碎片会出现,并使游戏崩溃 时间 . 有一个 "war story" interview 在YouTube上。 |