代码之家  ›  专栏  ›  技术社区  ›  AshleysBrain

什么是内存碎片?

  •  176
  • AshleysBrain  · 技术社区  · 14 年前

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

    • 什么是内存碎片?
    • 处理内存碎片的常用方法有哪些?

    也:

    • 我听说经常使用动态分配会增加内存碎片。这是真的吗?在C++环境下,我理解所有标准容器(STD::String,STD::vector等)使用动态内存分配。如果在整个程序(尤其是std::string)中都使用了这些,那么内存碎片更可能是一个问题吗?
    11 回复  |  直到 14 年前
        1
  •  332
  •   Steve Jessop    4 年前

    假设您有一个“大”(32字节)的可用内存:

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

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

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

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

    现在,试着分配16个字节。哎呀,我不能,尽管有将近两倍的免费。

    在具有虚拟内存的系统上,碎片问题比您想象的要小,因为大型分配只需要在内存中连续 事实上的 地址空间,不在 身体的 地址空间。所以在我的例子中,如果我有一个页面大小为2字节的虚拟内存,那么我可以毫无问题地进行16字节的分配。物理内存如下所示:

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

    而虚拟内存(要大得多)可能是这样的:

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

    内存碎片的典型症状是,你试图分配一个大的块,但你不能,即使你似乎有足够的内存可用。另一个可能的后果是进程无法将内存释放回操作系统(例如,因为它从操作系统分配的每个大块) malloc 等进行细分时,即使每个块的大部分现在都未使用,也会在其中保留一些内容。

    在C++工作中通过根据它们的大小和/或它们的预期寿命分配对象来防止内存碎片化的策略。因此,如果要创建大量对象并在以后一起销毁它们,请从内存池中分配它们。在它们之间执行的任何其他分配都不会来自池,因此不会位于它们之间的内存中,因此不会导致内存碎片化。或者,如果要分配许多相同大小的对象,则从同一个池中分配它们。那么,池中的一段可用空间永远不能小于您试图从该池中分配的大小。

    马洛克

    标准库并不比其他分配内存的库差,标准容器都有一个 Alloc 模板参数,如果绝对必要,您可以使用它来微调它们的分配策略。

        2
  •  86
  •   Mike Dinescu    14 年前

    什么是内存碎片?

    内存碎片是指当你的大部分内存被分配到大量不连续的块或块中时,你的总内存中有很大一部分没有被分配,但在大多数典型的情况下是不可用的。这会导致内存不足异常或分配错误(即malloc返回null)。

    最简单的思考方法是想象你有一堵空墙,你需要把照片放进去 大小不一的 打开。每张照片都有一定的尺寸,很明显你不能把它分成更小的几块,以使它合身。你需要在墙上有一个空的地方,图片的大小,否则你就放不起来。现在,如果你开始把图片挂在墙上,而你对如何排列它们不小心,你很快就会发现墙上部分地挂满了图片,即使你可能有空的地方,大多数新图片也放不下,因为它们比可用的地方大。你仍然可以挂很小的照片,但大多数都不适合。因此,你必须重新安排(压缩)那些已经在墙上腾出更多的空间。。

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

    一个迹象表明,你可能正在处理内存碎片是如果你得到许多分配错误,特别是当使用内存的百分比很高-但不是你还没有用完所有的内存-所以从技术上讲,你应该有足够的空间,你试图分配的对象。

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

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

    使用一个好的算法来分配内存。与其为许多小对象分配内存,不如为这些小对象的连续数组预分配内存。有时,在分配内存时有点浪费可以提高性能,还可以省去处理内存碎片的麻烦。

        3
  •  24
  •   Tyler McHenry    14 年前

    内存碎片和磁盘碎片的概念是一样的:它指的是由于使用中的区域没有足够紧密地组合在一起而浪费的空间。

    假设一个简单的玩具示例有10个字节的内存:

     |   |   |   |   |   |   |   |   |   |   |
       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
    

    现在,如果我们尝试分配一个四字节的数据块D,会发生什么?嗯,我们有四个字节的空闲内存,但我们没有四个字节 相邻的 字节的内存可用,所以我们不能分配D!这是对内存的低效使用,因为我们本应该能够存储D,但却无法。我们不能移动C来腾出空间,因为我们的程序中很可能有一些变量指向C,我们不能自动找到并更改所有这些值。

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

    在这个例子中,3个字节并不是一大堆浪费,但是考虑一个更病态的情况,在这个情况下,两个字节的分配在内存中相隔10兆字节,并且需要分配一个大小为10兆字节+1字节的块。你必须要求操作系统多提供超过10兆字节的虚拟内存才能做到这一点,尽管你已经离拥有足够的空间只有一个字节了。

    一般来说,分配的次数越少,内存碎片化的可能性就越小。然而,STL相当有效地处理了这个问题。如果您有一个字符串正在使用其当前分配的全部内容,并且您在其中附加了一个字符,那么它不会简单地重新分配到其当前长度加上一个字符,而是 双打

    尽管STL容器当然不会共享内存 之间

        4
  •  14
  •   Aman Shekhar    4 年前

    内存碎片是指内存变得不可用的问题,即使它在理论上是可用的。有两种碎片: 是已分配但无法使用的内存(例如,当内存分配为8字节块,但程序仅需要4字节时重复执行单个分配)。 问题是空闲内存被分成许多小块,这样虽然有足够的总空闲内存,但无法满足大型分配请求。

    • 如何判断内存碎片是否是我的应用程序的问题?什么样的项目最有可能受到影响?

    如果您的程序使用的系统内存比实际paylod数据需要的内存多得多,那么内存碎片就是一个问题(您已经排除了内存泄漏的可能性)。

    使用一个好的内存分配器。IIRC,那些使用“最佳匹配”策略的人在避免碎片化方面通常要高得多,如果慢一点的话。然而,研究也表明,对于任何分配策略,都存在病态的最坏情况。幸运的是,对于分配器来说,大多数应用程序的典型分配模式实际上是相对良性的。如果你对细节感兴趣的话,这里有一堆文件:

    • 保罗·R·威尔逊、马克·S·约翰斯顿、迈克尔·尼利和大卫·伯尔斯。动态存储分配:综述和评论。在1995年的法律程序中 内存管理国际研讨会,Springer Verlag LNCS,1995年
    • 马克·S·约翰斯顿,保罗·R·威尔逊。内存碎片问题:解决了吗? ACM SIG-PLAN通告,第34卷第3期,第26-36页,1999年
    • M.R.Garey、R.L.Graham和J.D.Ullman。内存分配算法的最坏情况分析。在第四届ACM计算理论年会上,1972年
        5
  •  9
  •   user184968 user184968    13 年前

    更新:
    Google TCMalloc: Thread-Caching Malloc
    人们发现 它很擅长处理碎片


    我一直在开发一个服务器应用程序,它在HP-UX 11.23/11.31 ia64上存在内存碎片问题。

    它看起来像这样。有一个进程进行了内存分配和释放,并运行了数天。即使没有内存泄漏,进程的内存消耗也在不断增加。

    关于我的经历。在HP-UX上,使用HP-UX gdb很容易找到内存碎片。设置一个断点,点击后运行以下命令: info heap 信息堆 . 如果堆的总大小较大,但单独分配的数量和大小相同,则很可能存在内存分配问题。如有必要,请检查几次。

    我改善现状的方法就是这样。在我用HP-UX gdb做了一些分析之后,我发现内存问题是由我使用 std::vector 用于存储数据库中某些类型的信息。 标准::向量 要求其数据必须保存在一个块中。我有一些基于 标准::向量 . 这些容器定期重新制造。在向数据库中添加新记录之后,通常会重新创建容器。而且由于重新创建的容器更大,它们无法容纳可用的可用内存块,运行时要求从操作系统中获得一个新的更大的块。因此,即使没有内存泄漏,进程的内存消耗也会增加。我换了集装箱后情况有所好转。而不是 我开始使用 std::deque 为数据分配内存的方式不同。

    Low-fragmentation Heap 它解决了大量小分配的问题。

    我的理解是,在STL密集型应用程序中,必须首先确定问题。内存分配器(如libc中的)实际上处理大量小分配的问题,这对于 std::string (例如,在我的服务器应用程序中,有许多STL字符串,但正如我从运行 信息堆 他们没有造成任何问题)。我的印象是,你需要避免频繁的大额分配。不幸的是,有些情况下,您无法避免它们,必须更改代码。正如我在我的案例中所说的,当我转到 标准::德克 . 如果你确定了你的记忆碎片,也许可以更准确地谈论它。

        6
  •  6
  •   Björn Pollex    13 年前

    内存碎片最有可能发生在分配和 许多大小不一的物体。假设内存中有以下布局:

    obj1 (10kb) | obj2(20kb) | obj3(5kb) | unused space (100kb)
    

    现在,什么时候 obj2 释放时,您有120kb的未使用内存,但无法分配120kb的完整块,因为内存是碎片化的。

    ring buffers object pools . 在STL的上下文中,方法 std::vector::reserve() 我可以帮忙。

        7
  •  6
  •   Stephen Kellett    9 年前

    关于内存碎片的一个非常详细的答案可以在这里找到。

    http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/

    这是我11年来为人们提供的关于记忆碎片问题的答案的总结软件验证.com

        8
  •  3
  •   Community Neeleshkumar S    7 年前

    什么是内存碎片?

    支离破碎的

    另一个不可避免但问题较少的碎片来源是,在大多数体系结构中,内存地址必须 char

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

    显而易见的答案是,你得到了一个内存不足的异常。

    显然,没有一种好的便携方式来检测C++应用程序中的内存碎片。看到了吗 this answer 更多细节。

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

        9
  •  3
  •   user455288    14 年前

    这是一个超级简化版的假人。

    这就是所谓的分裂。

        10
  •  2
  •   wheaties    14 年前

    当您想在堆中添加一个项目时,所发生的是计算机必须搜索空间来容纳该项目。这就是为什么不在内存池或池分配器上执行动态分配时会“减慢”速度。对于一个繁重的STL应用程序,如果您正在执行多线程,那么 Hoard allocator 或者 TBB Intel

    现在,当记忆支离破碎时,会发生两件事:

    1. 必须进行更多的搜索才能找到一个好的空间来粘贴“大”对象。也就是说,由于许多小对象分散在周围,在某些情况下很难找到一个连续的内存块(这些都是极端的)
    2. 内存不是一个容易读取的实体。处理器被限制在它们能容纳多少和在哪里。如果他们需要的项目是一个地方,而当前地址是另一个地方,他们可以通过交换页面来实现。如果您经常需要交换页面,处理速度可能会减慢(同样,这会影响性能的极端情况) virtual memory .
        11
  •  1
  •   Puppy    14 年前

    发生内存碎片是因为请求了不同大小的内存块。考虑100字节的缓冲区。您需要两个字符,然后是一个整数。现在释放这两个字符,然后请求一个新的整数-但是这个整数不能放在这两个字符的空间中。该内存不能被重用,因为它不在一个足够大的连续块中,无法重新分配。除此之外,还为char调用了大量分配器开销。

    基本上,在大多数系统中,内存只以一定大小的块出现。一旦将这些块拆分,在释放整个块之前,它们不能重新连接。这可能导致整个块在使用,而实际上只有一小部分块在使用。

    您应该记住的是,在32位x86桌面系统上,您拥有整个2GB的内存,这些内存被拆分为4KB的“页面”(非常确定所有x86系统上的页面大小都是相同的)。您必须调用一些omgwtfbbq片段才能出现问题。碎片化确实是一个过去的问题,因为现代堆对于绝大多数应用程序来说都过大,而且有很多系统能够承受它,比如托管堆。

        12
  •  0
  •   Thomas    4 年前

    什么样的项目最有可能受到影响?

    “元素:魔法之战” ,一款电脑游戏 Stardock

    这款游戏是为32位/2GB内存设计的,必须对内存管理进行大量优化,才能使游戏在2GB内存中运行。由于“优化”会导致不断的分配和取消分配,随着时间的推移,堆内存碎片会出现,并使游戏崩溃 时间 .

    有一个 "war story" interview 在YouTube上。

    推荐文章