【问题标题】:Does realloc actually shrink buffers in common implementations?realloc 在常见实现中实际上会缩小缓冲区吗?
【发布时间】:2011-11-17 21:04:43
【问题描述】:

在 Linux/Glibc、Windows/MSVC 和 BSD/Mac OS X 等常见实现中,会

void *p = malloc(N + M);  // assume this doesn't fail
p = realloc(p, N);        // nor this

对于NM > 0,实际上缩小了mallocrealloc 调用中返回的缓冲区,从某种意义上说,最多M 字节可能会返回到空闲列表?更重要的是,它是否有可能重新分配缓冲区?

我想知道,因为我刚刚在numpy.ndarray 之上实现了动态数组,并且我正在做一个resize,它调用realloc,以获得正确的最终大小。我也许可以跳过最后的 resize 作为优化(以永久过度分配为代价),我想知道这是否值得尝试。

【问题讨论】:

  • @jterrace 你将如何测试内部实现是否缩小了你拥有的内存大小?
  • 在某些实现中,当您调用 mallocrealloc...时,甚至不会分配内存...
  • 看看指针的值是否真的改变了会很有趣。比较p 前后的值。如果它确实发生了变化,那么您就知道发生了某事
  • @Joe 是的,但也请记住,如果它不改变,你知道某事正在发生。
  • 你不能假设realloc() 在这种情况下不会重新分配缓冲区。原因之一是realloc() 可能会选择这样做是为了尽量减少碎片。有实现依赖。最好在这些平台上查看realloc() 的官方接口文档。为了实现最大的可移植性,建议不要假设超出文档“按合同”提供的任何内容。

标签: python c memory-management numpy malloc


【解决方案1】:

我可以说一下 Linux/glibc。 在源代码中,它包含这样的 cmets:

如果n 的字节数少于p 已保存的字节数,则新未使用的
如果可能,空间会被截断并释放。

如果你看一下 glibc 的代码,它包含这样的代码行:

remainder_size = newsize - nb;

if (remainder_size < MINSIZE) { /* not enough extra to split off */
  set_head_size(newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
  set_inuse_bit_at_offset(newp, newsize);
}
else { /* split remainder */
  remainder = chunk_at_offset(newp, nb);
  set_head_size(newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
  set_head(remainder, remainder_size | PREV_INUSE |
       (av != &main_arena ? NON_MAIN_ARENA : 0));
  /* Mark remainder as inuse so free() won't complain */
  set_inuse_bit_at_offset(remainder, remainder_size);
 #ifdef ATOMIC_FASTBINS
  _int_free(av, remainder, 1);
 #else
  _int_free(av, remainder);
 #endif
}

nb - 你想要的字节数,这里是newsize,应该叫做oldsize。 因此,如果可能,它会尝试释放多余的部分。

关于 Mac OSX。更准确地说是关于magazine_malloc,Apple 对malloc 的当前实现。详情请见http://cocoawithlove.com/2010/05/look-at-how-malloc-works-on-mac.html

realloc 调用 zone realloc 方法,我看到的当前实现是szone_realloc。 对于不同的分配大小存在不同的代码,但算法总是相同的:

if (new_good_size <= (old_size >> 1)) {
            /*
             * Serious shrinkage (more than half). free() the excess.
             */
            return tiny_try_shrink_in_place(szone, ptr, old_size, new_good_size);
} else if (new_good_size <= old_size) {
            /* 
             * new_good_size smaller than old_size but not by much (less than half).
             * Avoid thrashing at the expense of some wasted storage.
             */
             return ptr;
}

如您所见,它的实现会检查new_size &lt;= old_size / 2,如果是则释放内存,否则它什么也不做。

【讨论】:

  • 什么是main_arena
  • @jrwren 您可以查看 glibc gnu.org/software/libc 的源代码并找到您问题的答案。
【解决方案2】:

它是否值得取决于对象将存在多长时间以及减少其内存占用对应用程序的重要性。没有一个正确的通用答案。

通用内存分配器通常假定调用者知道块的先前大小,并且只有在他们确实知道他们想要缩小块时才会调用realloc。如果块已经超过 128 字节并且重新分配将释放至少 1KB 或至少等于块当前分配大小的 1/4 的字节数,我查看的最后一个愿意缩小块。它针对大容量服务器应用程序进行了调整,在这些应用程序中,对象通常不会停留很长时间,并且为已知存在很长时间的对象提供了特殊的“正确大小”操作。

【讨论】:

    【解决方案3】:

    是的,他们有。但是标准并没有说明任何强制要求。因此,您必须使用您的目标libc 检查它。您可以通过查看代码(如果可用)或编写测试程序来做到这一点。测试程序的想法是这样的 - 你分配相对较大的块(比如 10K),然后尝试使用 realloc 缩小一半,并使用 malloc 分配一些小块。如果新返回的地址在您第一次分配的范围内,那么您的realloc 会缩小,否则不会。

    【讨论】:

    • 如果新返回的地址在您第一次分配的范围内,那么您的 realloc 会缩小,否则它可能会也可能不会。第二个malloc 可能有理由分配之前缩小的区域以外的地方,即使它可用。
    猜你喜欢
    • 2014-07-14
    • 2020-11-22
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    • 2015-03-29
    相关资源
    最近更新 更多