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

有效内存再分配问题

  •  0
  • luiscubal  · 技术社区  · 16 年前

    我在堆上还有一个区域(我称之为“页面”)(分配有malloc(region\u SIZE),其中region\u SIZE>=最大对象大小)。
    我一直在该页中保留空间,直到填充的空间等于页大小(或至少得到>页面大小-最大对象大小)。

    现在,我想分配更多的内存。显然我以前的“页面”还不够。所以我至少有两个选择:

    1. 分配一个新的“页面”(page2)并将新对象放在那里。

    1. 使用第一种方法,我会看到我已经填充了多少,然后将我的新对象放在那里(并将对象的大小添加到填充的内存变量中)。
    2. 使用第二种方法,我会有一个列表(向量?数组?)。

    最终,我也需要一个释放内存的方法,但我能找到那部分。

    所以我的问题是: 解决这样的问题最有效的方法是什么?是选项1、选项2还是我在这里没有考虑过的其他选项?一个小的基准是否需要/足够为现实世界的情况下得出结论? 我知道不同的操作可能会有不同的表现,但我正在寻找一个总体指标。

    6 回复  |  直到 16 年前
        1
  •  1
  •   madmik3    16 年前

    根据我的经验,选项2更容易使用,开销最小。重新分配 没有 保证它会增加现有内存的大小。实际上,几乎从来没有。如果您使用它,您将需要返回并重新映射所有旧对象。这就需要你记住每个分配的对象在哪里。。。那可能比头顶高出一吨。

    但如果不知道你使用的是什么标准,就很难确定“最有效”。

    对于每个分配,确定分配对象的大小。

    我看一个链接列表的自由对象的大小,看看是否有任何东西已被释放,如果是这样,采取第一个自由

    2在查找表中查找,如果找不到

    2.1分配一个由N个被分配大小的对象组成的数组。

    3.1如果数组已满,请添加新页。

    N个对象可以通过编程来实现。如果您知道您有一百万个16字节的对象,您可能希望N稍微高一点。

    免费:

    确定对象的大小,将其添加到空闲链接列表中。

    如果分配的对象的大小小于指针的大小,则链接列表不需要产生任何内存开销。只需使用已经分配的内存来存储节点。

        2
  •  0
  •   MAK    16 年前

    您的问题不清楚为什么需要提前分配一大块内存,而不是根据需要为每个对象分配内存。我假设您将它用作连续数组。不然的话,这样做更有意义 malloc 每个对象所需的内存。

    page2 ). 因此,它不再位于相邻的块上,并且不能将这两个块用作一个数组的一部分。

    realloc 另一方面,分配一个连续的内存块。您可以将它用作单个数组,并执行各种各样的指针算法,如果存在单独的块,则这是不可能的。 重新分配

    所以,如果你把它当作一个数组, 基本上是更好的选择。否则,就没什么问题了 . 实际上,你可能想用 马洛克

        3
  •  0
  •   the_void    16 年前

    你还没有给出你正在试验的平台的任何细节。有一些性能差异 realloc 之间 Linux Windows

    重新分配 可能需要分配一个 内存块,如果它不能 成长 当前的一个和 复制旧内存 到新的,这是 昂贵的 . 如果你真的不需要 相邻的 重新分配

    我的建议是使用第二种方法,或者使用自定义分配器(您可以实现一个简单的 buddy allocator [2]

    您还可以使用更高级的内存分配器,如

        4
  •  0
  •   ShinTakezou    16 年前

    realloc 会被malloc方法“打败”,但要说多少,你应该做测试(我认为在内存请求完成时,系统是如何工作的存在偏见)。

    根据您期望执行realloc/malloc的次数,它可能是一个有用的想法,也可能是一个无用的想法。反正我也会用malloc。

    自由战略取决于实施。要将所有页面作为一个整体释放,只需“遍历”它们就足够了;我不使用数组,而是使用链接的“pages”:将sizeof(void*)添加到“page”大小,您可以使用额外的字节来存储指向下一页的指针。

    如果您必须释放一个位于其中一个页面中任意位置的对象,它会变得稍微复杂一些。我的想法是保留一个非连续自由“块”/“槽”列表(适合容纳任何对象)。当请求一个新的“块”时,首先从这个列表中弹出一个值;如果它是空的,那么您将在最后一个正在使用的页面中获得下一个“slot”,并最终触发一个新页面。释放一个对象,意味着把空槽地址放在堆栈/列表中(不管你喜欢用什么)。

        5
  •  0
  •   Jens Gustedt    16 年前

    shm_open . 这样一个区域在您访问它之后就被初始化为零,但是如果您不只是保留虚拟内存中的地址范围,那么您永远不会访问的AIK页是免费的。因此,您可以在执行的开始(比您需要的更多)保留一大块内存,然后从一开始就逐步填充它。

        6
  •  0
  •   Dummy00001    16 年前

    选项1.为了提高效率,新的\u大小必须非线性地依赖于旧的大小。否则,由于冗余复制,您可能会遇到realloc()的O(n^2)性能。我通常是这样 new_size = old_size + old_size/4 (增加25%)理论上最好 new_size = old_size*2 在最坏的情况下可能会保留太多未使用的内存。

    最后,这完全取决于分配新对象的频率以及如何处理释放。如果用#1分配大量数据,则在展开时会有一些冗余的复制,但释放非常简单,因为所有对象都在同一页中。如果您需要释放/重用对象,那么使用#2您将花费一些时间浏览页面列表。

    根据我的经验#2更好,因为移动大内存块可能会增加堆碎片的速率。#2还允许使用指针,因为对象不会更改其在内存中的位置(不过对于某些应用程序,我更喜欢使用pool#id/索引对,而不是原始指针)。如果以后遍历页面成为一个问题,它可能会过于优化。