malloc和free的不同行为?

时间:2017-10-01 12:48:49

标签: c visual-studio cygwin malloc free

我有一个用C ++实现的树。

当我运行创建树的代码并使用 malloc()插入大约5,000,000个内部密钥时,它将需要 200MB (如所示)任务管理器)当我使用 Visual Studio 2013 运行时。

当我在每个节点上使用 free()释放树时,它将返回到 0.5MB 左右。直到现在一切正常。

现在当我使用 cygwin 编译并耗尽Visual Studio时,树的大小变得像 80MB (这是可以的,因为visual studio添加了调试信息和其他东西),但是当我释放树时,它的大小没有任何变化!!!

简而言之:在Visual Studio中运行时,释放树会将程序的大小返回到原始大小0.5MB但是当我编译并在Visual Studio外部运行相同的代码时(使用cygwin)然后释放树不会从程序大小改变任何东西仍然是80MB。

为什么会发生这种情况?

更新

释放树后,我尝试在树中再次插入相同的5,000,000个int键。在cygwin的情况下,它没有使用额外的内存,即相同的80MB,这意味着反馈指出(感谢大家+1)内存被释放但没有返回到操作系统。

现在的问题是: 如果我在记忆中有大量的分配,那么当我获得自由时,我不愿意将该自由区域保留在程序中。那么我如何强制 cygwin将其返回给操作系统?

3 个答案:

答案 0 :(得分:2)

不同的malloc表现不同。 Microsoft可以使用原始堆来分配和释放项目。

似乎MS在直接原始堆上使用内存(HeapAlloc / HeapFree)。

但是cygwin正在使用本地托管内存系统。

这意味着通过HeapAlloc / HeapFree释放将内存返回给操作系统,并释放相关资源。

在cygwin上调用free,将返回要重用的内存,但不会将其返回给操作系统。

管理大量内存

如果您需要处理大量内存,那么最好通过与操作系统进行迭代并调用其原始函数来直接管理它们。例如,在Windows上,您可以调用HeapCreate,创建一个单独的堆,并使用HeapAlloc在单独的大块分配中进行分配,并使用HeapFree释放它们。

完成块后,可以使用HeapDestroy释放所有内存。

答案 1 :(得分:1)

由于需要与其他进程交互(例如,与向实际管理硬件资源的特权设备驱动程序发出请求相关联的上下文切换),动态请求主机系统获取内存并释放内存可能是昂贵的操作。 / p>

为了减少这种性能命中,malloc()(和calloc()等)和free()可以在内部管理已分配内存池,以避免花费去每次请求或释放内存时的操作系统。例如,如果调用free(),它可能只是更改内部数据结构以记录程序不再使用的内存块,但实际上并未通知操作系统内存已释放。

malloc()也可能故意过度分配。例如,它可以一次从主机系统请求几千字节,即使程序请求少得多,然后阻止阻塞多个malloc()调用,而不是每次向操作系统发出请求致电malloc()

malloc()free()以这种方式工作,就操作系统而言,程序的内存使用量可能不会因调用free()而减少(至少直到free()的代码实际上根据自己的标准决定将内存返回给主机系统。在malloc()的多次调用中,即使程序多次调用malloc()而没有调用{{1},多次调用free(),内存使用率也会大幅上升,然后趋于平稳。 }。

答案 2 :(得分:0)

通用内存分配器使用各种启发式方法来尝试和优化公共分配/解除分配模式,但它们必须正确处理所有情况。

在您的特定情况下,如果您的问题具有以下属性,您可能有资格获得更简单的方法:

  • 您分配了许多具有相同大小的对象。
  • 您将它们分配到一个阶段。
  • 您可以同时释放所有对象

您可以使用自定义分配器来批量分配和释放节点。这是一个例子:

#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node *left, right;
} Node;

typedef struct NodeBlock {
    struct NodeBlock *next;
    size_t count, size;
    Node a[32768];
} NodeBlock;

Node *alloc_node(NodeBlock **node_heapp) {
    NodeBlock *ptr = *node_heap;
    if (!ptr || ptr->count == ptr->size) {
        ptr = calloc(sizeof(*ptr, 1);
        if (ptr == NULL)
            return NULL;
        ptr->size = sizeof(ptr->a) / sizeof(ptr->a[0]);
        ptr->next = *node_heap;
        *node_heap = ptr;
    }
    return &ptr->a[ptr->count++];
}

void free_nodes(NodeBlock **node_heapp) {
    while (*node_heapp) {
        NodeBlock *ptr = *node_heap;
        *node_heap = ptr->next;
        free(ptr);
    }
}

上面的代码大批量分配节点:

  • 分配效率更高,因为它只进行很少的系统调用,并使用一个简单的方法来返回下一个可用的插槽。
  • 开销非常小。
  • deallocation也非常快,因为不需要走树来解除分配单个节点。
  • 它在Unix系统上也可能具有良好的属性,因为malloc()可能会对大型请求使用mmap(),在释放时将内存放回系统。如果您没有观察到这一点,请尝试增加阻塞因子(Node数组a的长度)。