什么是内存碎片?

我曾经在C ++动态内存分配的上下文中听过“内存碎片”这个术语。 我发现了一些关于如何处理内存碎片的问题,但是找不到直接处理内存碎片的问题。 所以:

  • 什么是内存碎片?
  • 如何判断内存碎片对我的应用程序是否有问题? 什么样的节目最有可能遭受?
  • 处理内存碎片的常用方法有哪些?
  • 也:

  • 我听说使用动态分配可能会增加内存碎片。 这是真的? 在C ++的上下文中,我理解所有的标准容器(std :: string,std :: vector等)都使用动态内存分配。 如果这些在整个程序中使用(尤其是std :: string),内存碎片更可能是一个问题?
  • 内存碎片如何在STL重的应用程序中处理?

  • 想象一下,你有一个“大”(32字节)的可用内存空间:

    ----------------------------------
    |                                |
    ----------------------------------
    

    现在,分配一些(5分配):

    ----------------------------------
    |aaaabbccccccddeeee              |
    ----------------------------------
    

    现在,免费的前四个分配,但不是第五个:

    ----------------------------------
    |              eeee              |
    ----------------------------------
    

    现在,尝试分配16个字节。 哎呀,我不能,即使有几乎是免费的两倍。

    在具有虚拟内存的系统上,碎片的问题不像您想象的那么严重,因为大量分配只需要在虚拟地址空间中连续,而不是在物理地址空间中。 所以在我的例子中,如果我的页面大小为2字节的虚拟内存,那么我可以使我的16字节分配没有问题。 物理内存看起来像这样:

    ----------------------------------
    |ffffffffffffffeeeeff            |
    ----------------------------------
    

    而虚拟内存(更大)可能是这样的:

    ------------------------------------------------------...
    |              eeeeffffffffffffffff                   
    ------------------------------------------------------...
    

    内存碎片的典型症状是,尽管您似乎有足够的内存空间,但您仍尝试分配一个大块,但不能。 另一个可能的后果是该进程无法将内存释放回操作系统(因为从OS分配的所有块中仍有一些对象正在使用,即使这些块现在大部分都未使用)。

    通过根据其大小和/或预期的生命周期从不同区域分配对象来防止C ++中内存碎片的策略。 因此,如果您要创建大量对象并在稍后将它们全部销毁,请从内存池中分配它们。 你在它们之间做的任何其他分配不会来自池中,因此它们不会位于它们之间的内存中,因此内存不会因此而碎片化。

    一般来说,您不必担心,除非您的程序长时间运行并且执行大量分配和释放。 这就是当你遇到最危险的短寿命和长寿命物体的混合体,但即使如此, malloc也会尽力提供帮助。 基本上,忽略它直到你的程序出现分配失败或意外导致系统内存不足(在测试中捕获它,首选!)。

    标准库不会比分配内存的其他任何东西都差,并且标准容器都有一个Alloc模板参数,您可以在绝对必要时使用它来微调其分配策略。


    什么是内存碎片?

    内存碎片是当大部分内存分配到大量非连续块或块中时 - 留下很大比例的总内存未分配,但在大多数典型场景中不可用。 这会导致内存不足或分配错误(即malloc返回null)。

    想想这个最简单的方法就是想象你有一个很大的空墙,你需要放置不同尺寸的图片。 每张照片都占用一定的尺寸,显然不能将其分割成更小的部分以使其适合。 你需要在墙上留下一个空白点,图片的大小,否则你就无法忍受。 现在,如果你开始在墙上挂图片,而且你不仔细考虑如何安排它们,那么很快你会得到一个部分被图片覆盖的墙,即使你可能有空白点,大多数新图片都不适合因为它们比可用的点大。 你仍然可以挂非常小的照片,但大多数不适合。 所以你必须重新安排(紧凑)已经在墙上的那些以腾出更多的空间。

    现在,想象墙壁是你的(堆)内存,图片是对象..这就是内存碎片..

    如何判断内存碎片对我的应用程序是否有问题? 什么样的节目最有可能遭受?

    如果你有很多分配错误,尤其是在使用内存百分比很高的时候,你可能正在处理内存碎片的一个信号迹象 - 但是你还没有用完所有的内存 - 所以在技术上你应该有足够的空间为您尝试分配的对象。

    当内存严重分散时,内存分配可能需要更长的时间,因为内存分配器必须做更多的工作才能为新对象找到合适的空间。 如果反过来你有很多内存分配(你可能会做内存碎片),分配时间甚至可能导致明显的延迟。

    处理内存碎片的常用方法有哪些?

    使用一个好的算法来分配内存。 不是为很多小对象分配内存,而是为这些较小对象的连续数组预先分配内存。 有时在分配内存时有点浪费,可能会影响性能,并可能为您节省处理内存碎片的麻烦。


    内存碎片与磁盘碎片的概念相同:它指的是空间被浪费,因为正在使用的区域未被紧密包装在一起。

    假设一个简单的玩具例子,你有十个字节的内存:

     |   |   |   |   |   |   |   |   |   |   |
       0   1   2   3   4   5   6   7   8   9
    

    现在让我们分配三个三字节块,名称A,B和C:

     | A | A | A | B | B | B | C | C | C |   |
       0   1   2   3   4   5   6   7   8   9
    

    现在释放块B:

     | A | A | A |   |   |   | C | C | C |   |
       0   1   2   3   4   5   6   7   8   9
    

    现在如果我们尝试分配一个4字节块D,会发生什么? 那么,我们有四个字节的内存空间,但我们没有四个连续的内存空间,所以我们不能分配D! 这是内存使用效率低下的原因,因为我们应该能够存储D,但是我们无法做到。 我们不能移动C来腾出空间,因为我们的程序中很可能有一些变量指向C,我们不能自动查找和更改所有这些值。

    你怎么知道这是一个问题? 嗯,最大的迹象是你的程序的虚拟内存大小要比你实际使用的内存大得多。 在一个现实世界的例子中,你会有超过十个字节的内存,所以D会被分配开始一个字节9,并且字节3-5将保持不被使用,除非你稍后分配了三个字节长或更小的内容。

    在这个例子中,3个字节并不是一个很大的浪费,但是考虑一个更为病态的情况,例如,两个字节的两个分配在内存中分开十兆字节,并且您需要分配一个大小为10兆字节的块+ 1个字节。 你必须去问操作系统超过十兆字节的虚拟内存来做到这一点,即使你只有一个字节害羞已经有足够的空间。

    你如何防止它? 当你经常制造和销毁小物体时,往往会出现最糟糕的情况,因为这样往往会产生“瑞士奶酪”的效果,许多小物体被许多小孔分隔开来,因此无法在这些孔中分配更大的物体。 当你知道你会这样做时,一个有效的策略是预先为你的小对象分配一大块内存作为池,然后手动管理该块内的小对象的创建,而不是让它默认分配器处理它。

    一般来说,你做的分配越少,内存分解的可能性就越小。 但是,STL相当有效地处理这个问题。 如果你有一个字符串正在使用当前分配的全部内容,并且向它附加了一个字符,它不会简单地重新分配给它的当前长度加上一个字符,而是将其长度加倍。 这是“频繁小额分配池”战略的一个变种。 该字符串抓取大量内存,以便可以有效地处理重复的小尺寸增量,而无需重复进行小的重新分配。 所有的STL容器实际上都是这样做的,所以一般来说你不必过分担心自动重新分配STL容器造成的碎片。

    虽然STL容器当然不会在彼此之间集中内存,但是如果要创建许多小容器(而不是少数容器经常调整大小),则可能不得不考虑以同样的方式防止碎片对于任何经常创建的小对象,STL与否。

    链接地址: http://www.djcxy.com/p/78713.html

    上一篇: What is memory fragmentation?

    下一篇: Ownership/delete'ing the facet in a locale (std::locale)