Ash*_*ain 190 c++ memory heap fragmentation
我听说在C++动态内存分配的上下文中使用了几次"内存碎片"这个术语.我发现了一些关于如何处理内存碎片的问题,但找不到直接处理它本身的问题.所以:
也:
Ste*_*sop 294
想象一下,你有一个"大"(32字节)的可用内存扩展:
----------------------------------
| |
----------------------------------
Run Code Online (Sandbox Code Playgroud)
现在,分配一些(5个分配):
----------------------------------
|aaaabbccccccddeeee |
----------------------------------
Run Code Online (Sandbox Code Playgroud)
现在,释放前四个分配但不是第五个:
----------------------------------
| eeee |
----------------------------------
Run Code Online (Sandbox Code Playgroud)
现在,尝试分配16个字节.哎呀,我不能,尽管有近两倍的免费.
在具有虚拟内存的系统上,碎片问题不像您想象的那么严重,因为大型分配只需要在虚拟地址空间中连续,而不是在物理地址空间中.所以在我的例子中,如果我有一个页面大小为2字节的虚拟内存,那么我可以毫无问题地进行16字节分配.物理内存看起来像这样:
----------------------------------
|ffffffffffffffeeeeff |
----------------------------------
Run Code Online (Sandbox Code Playgroud)
而虚拟内存(更大)可能看起来像这样:
------------------------------------------------------...
| eeeeffffffffffffffff
------------------------------------------------------...
Run Code Online (Sandbox Code Playgroud)
内存碎片的典型症状是你尝试分配一个大块而你不能,即使你似乎有足够的内存空闲.另一个可能的后果是进程无法将内存释放回OS(因为在从OS分配的所有块中仍然使用了某些对象,即使这些块现在大部分未使用).
通过根据不同区域的大小和/或预期生命周期分配对象来防止C++中的内存碎片的策略.因此,如果您要创建大量对象并在以后将它们全部销毁,请从内存池中分配它们.你在它们之间进行的任何其他分配都不会来自池,因此它们不会位于它们之间的内存中,因此内存不会因此而被分段.
通常你不需要担心它,除非你的程序长期运行并且进行大量的分配和释放.当你拥有短寿命和长寿命物体的混合物时,你最有风险,但即便如此,malloc
也会尽力提供帮助.基本上,忽略它,直到你的程序有分配失败或意外导致系统内存不足(在测试中捕获这个,优先!).
标准库并不比分配内存的任何其他东西差,标准容器都有一个Alloc
模板参数,如果绝对必要,您可以使用它来微调其分配策略.
Mik*_*scu 70
什么是内存碎片?
内存碎片是指您的大部分内存分配在大量非连续块或块中 - 大部分内存未分配,但大多数情况下无法使用.这会导致内存不足异常或分配错误(即malloc返回null).
考虑这个的最简单的方法是想象你有一个很大的空墙,你需要把不同大小的图片放在上面.每张照片占用一定的尺寸,你显然不能将它分成小块以使其适合.你需要在墙上留一个空白点,图片的大小,否则你就无法忍受.现在,如果你开始在墙上挂图片并且你不小心你如何安排它们,你很快会得到一个部分覆盖着图片的墙,即使你可能有空白点,大多数新图片也不适合因为它们比可用的点大.你仍然可以挂小图片,但大多数图片都不合适.所以你必须重新安排(紧凑)已经在墙上的那些,以腾出更多空间..
现在,想象一下墙是你的(堆)内存,图片是对象..这是内存碎片..
如何判断内存碎片是否对我的应用程序有问题?什么样的计划最容易受到影响?
你可能正在处理内存碎片的一个迹象就是如果你得到很多分配错误,特别是当使用内存的百分比很高时 - 但是你还没有用尽所有内存 - 所以从技术上来说你应该有足够的空间对于您要分配的对象.
当内存严重碎片化时,内存分配可能需要更长的时间,因为内存分配器必须做更多工作才能为新对象找到合适的空间.如果反过来你有很多内存分配(你可能会在你最终导致内存碎片的情况下这样做),分配时间甚至可能导致明显的延迟.
处理内存碎片的常用方法有哪些?
使用一个好的算法来分配内存.而不是为许多小对象分配内存,为这些较小对象的连续数组预先分配内存.分配内存时有时会有点浪费,可以提高性能,可以省去处理内存碎片的麻烦.
Tyl*_*nry 23
内存碎片与磁盘碎片的概念相同:它指的是浪费的空间,因为使用的区域没有足够紧密地打包在一起.
假设一个简单的玩具示例,你有10个字节的内存:
| | | | | | | | | | |
0 1 2 3 4 5 6 7 8 9
Run Code Online (Sandbox Code Playgroud)
现在让我们分配三个三字节块,名称A,B和C:
| A | A | A | B | B | B | C | C | C | |
0 1 2 3 4 5 6 7 8 9
Run Code Online (Sandbox Code Playgroud)
现在解除分配块B:
| A | A | A | | | | C | C | C | |
0 1 2 3 4 5 6 7 8 9
Run Code Online (Sandbox Code Playgroud)
现在如果我们尝试分配一个四字节的块D会发生什么?好吧,我们有四个字节的内存空闲,但我们没有四个连续的内存字节,所以我们不能分配D!这是对内存的低效使用,因为我们应该能够存储D,但我们无法做到.我们不能移动C来腾出空间,因为我们程序中的某些变量很可能指向C,我们无法自动查找和更改所有这些值.
你怎么知道这是一个问题?好吧,最大的迹象是你的程序的虚拟内存大小远远大于你实际使用的内存量.在一个实际的例子中,你将有超过十个字节的内存,所以D只会从字节9开始分配,而字节3-5将保持未使用状态,除非你后来分配了三个字节长或更小的东西.
在这个例子中,3个字节并不是浪费,但考虑一个更加病态的情况,其中两个字节的两个分配,例如,内存相隔10兆字节,你需要分配一个大小为10兆字节的块+ 1个字节.你必须要求操作系统获得超过10兆字节的虚拟内存才能做到这一点,即使你只有一个字节还没有足够的空间.
你怎么防止它?当您经常创建和销毁小物体时,最坏的情况往往会产生,因为这会产生"瑞士奶酪"效应,许多小物体被许多小孔分开,从而无法在这些洞中分配更大的物体.当你知道你将会这样做时,一个有效的策略是预先分配一大块内存作为小对象的池,然后手动管理该块中的小对象的创建,而不是让它默认分配器处理它.
通常,您执行的分配越少,内存碎片的可能性就越小.但是,STL相当有效地处理了这个问题.如果你有一个字符串使用其当前分配的全部并且你附加一个字符,它不会简单地重新分配到它的当前长度加一,它的长度翻倍.这是"频繁小额分配池"策略的变体.该字符串占用了大量内存,因此它可以有效地处理重复的小幅增加,而无需重复进行小的重新分配.实际上所有的STL容器都是这样做的,所以通常你不必过于担心自动重新分配STL容器造成的碎片.
虽然STL容器当然不会在彼此之间汇集内存,但是如果你要创建许多小容器(而不是经常调整大小的几个容器),你可能不得不关心自己防止碎片的方式与你一样适用于任何频繁创建的小对象,STL与否.
Mic*_*rdt 14
- 什么是内存碎片?
内存碎片是内存变得无法使用的问题,即使它在理论上是可用的.碎片有两种类型:内部碎片是分配但不能使用的内存(例如,当存储器以8字节块分配时,但程序只需要4个字节就会重复执行单个归属).外部碎片是自由内存被分成许多小块的问题,因此尽管有足够的整体可用内存,但是无法满足大的分配请求.
- 如何判断内存碎片是否对我的应用程序有问题?什么样的计划最容易受到影响?
如果你的程序使用的系统内存比实际的paylod数据要多得多(而且你已经排除了内存泄漏),那么内存碎片是一个问题.
- 处理内存碎片的常用方法有哪些?
使用一个好的内存分配器.IIRC,那些使用"最适合"策略的人通常在避免碎片方面要优越得多,如果慢一点的话.但是,已经表明,对于任何分配策略,都存在病态最坏的情况.幸运的是,大多数应用程序的典型分配模式实际上对于分配器来说是相对良性的.如果您对细节感兴趣,那里有很多论文:
更新:
Google TCMalloc:Thread-Caching Malloc
已经发现它在处理长时间运行的流程中非常擅长处理碎片.
我一直在开发一个服务器应用程序,该应用程序在HP-UX 11.23/11.31 ia64上存在内存碎片问题.
看起来像这样.有一个进程进行了内存分配和解除分配并运行了几天.即使没有内存泄漏,该进程的内存消耗也在不断增加.
关于我的经历.在HP-UX上,使用HP-UX gdb很容易找到内存碎片.您设置了一个断点,当您点击它时,您运行此命令:info heap
并查看进程的所有内存分配和堆的总大小.然后你继续你的程序,然后一段时间后你再次达到了断点.你再做一次info heap
.如果堆的总大小较大但单独分配的数量和大小相同,则可能存在内存分配问题.如有必要,请在前几次检查.
我改善这种情况的方法是这样的.在我使用HP-UX gdb进行一些分析后,我发现内存问题是由于我用于std::vector
存储数据库中的某些类型的信息而引起的.std::vector
要求其数据必须保存在一个块中.我有一些基于的容器std::vector
.这些容器定期重建.通常情况下,将新记录添加到数据库中,然后重新创建容器.而且由于重新创建的容器更大,它们不适合可用的可用内存块,运行时需要从操作系统中获取更大的块.因此,即使没有内存泄漏,该进程的内存消耗也会增加.当我更换容器时,我改善了这种情况.而不是std::vector
我开始使用std::deque
它有不同的方式为数据分配内存.
我知道在HP-UX上避免内存碎片的一种方法是使用Small Block Allocator或使用MallocNextGen.在RedHat Linux上,默认的分配器似乎可以很好地分配很多小块.在Windows上有它Low-fragmentation Heap
并且它解决了大量小分配的问题.
我的理解是,在STL繁重的应用程序中,您首先要发现问题.内存分配器(如在libc中)实际上处理了许多小分配的问题,这是典型的std::string
(例如在我的服务器应用程序中有很多STL字符串,但正如我从运行中看到的那样,info heap
它们没有引起任何问题).我的印象是你需要避免频繁的大量分配.不幸的是,有些情况下您无法避免它们并且必须更改代码.正如我在我的情况中说的那样,我改用了切换到的情况std::deque
.如果你确定你的记忆碎片,可能会更准确地谈论它.
分配和释放许多不同大小的对象时,最有可能发生内存碎片.假设您在内存中具有以下布局:
obj1 (10kb) | obj2(20kb) | obj3(5kb) | unused space (100kb)
Run Code Online (Sandbox Code Playgroud)
现在,当obj2
发布时,你有120kb的未使用内存,但是你不能分配120kb的完整块,因为内存是碎片化的.
避免这种影响的常用技术包括环形缓冲区和对象池.在STL的上下文中,类似的方法std::vector::reserve()
可以提供帮助.
关于内存碎片的非常详细的答案可以在这里找到.
http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/
这是11年内存碎片问题的高潮,我一直在向人们提供有关内存碎片的问题,请访问softwareverify.com
归档时间: |
|
查看次数: |
69307 次 |
最近记录: |