使用allocator来替换malloc()/ free()?

时间:2011-06-19 13:44:56

标签: c++ memory malloc

是否有任何可移植的方法来替换类似STL的分配器的包装器来替换malloc()/ free()的使用?

上下文:我有一个C库,它允许指定自定义的malloc()/ free() - 类似于内存管理的函数,并且在多线程上下文中使用。为了找到一个好的多线程分配器,我发现GCC-libstdc ++的mt_alloc对我的工作负载表现非常好。现在我想在所说的C库中使用它,但是怎么做呢?

我看到的主要问题是deallocate()函数,与free()相反,除了它的地址外,它还占用了已分配内存块的大小。因此,我需要以某种方式跟踪与每个内存分配相关联的大小,以便在释放内存时可以将其反馈给deallocate()。我想要解决的最简单的解决方案是在内存块的开头存储已分配内存的大小,但后来我不确定如何解决可能出现的对齐问题。

我有什么简单的解决方案吗?

4 个答案:

答案 0 :(得分:4)

在我的平台上,malloc确保分配的内存以8字节边界对齐。要模仿此行为,请使用allocator<uint64_t>

#include <stdint.h>
#include <ext/mt_allocator.h>

static __gnu_cxx::__mt_alloc<uint64_t> theAllocator;

void* mtmalloc(size_t size)
{
    // Divide size by sizeof(uint64_t) and round up
    size_t payloadElementCount = (size + sizeof(uint64_t) - 1) /
                                 sizeof(uint64_t);

    // Add an extra uint64_t to store the chunk size
    size_t chunkElementCount = 1 + payloadElementCount;

    // Allocate the chunk
    uint64_t* chunk = theAllocator.allocate(chunkElementCount);

    // Store the chunk size in the first word
    chunk[0] = chunkElementCount;

    // Return a pointer past where the chunk size is stored
    return static_cast<void*>(chunk + 1);
}

void mtfree(void* pointer)
{
    // The chunk begins one word before the passed in pointer
    uint64_t* chunk = static_cast<uint64_t*>(pointer) - 1;

    // Retrieve the chunk size
    size_t chunkElementCount = chunk[0];

    // Deallocate the chunk
    theAllocator.deallocate(chunk, chunkElementCount);
}

int main()
{
    int* array = (int*)mtmalloc(sizeof(int) * 4);
    array[0] = 0;
    array[1] = 1;
    array[2] = 2;
    array[3] = 3;
    mtfree(array);
}

对于您的平台,请使用相应的类型替换uint64_t

你应该用像Valgrind这样的东西进行测试,以确保没有内存泄漏!


而不是uint64_t,您可以使用GCC的__BIGGEST_ALIGNMENT__和Boost的aligned_storage type trait来获取可移植到GCC编译器的解决方案:

typedef boost::aligned_storage<__BIGGEST_ALIGNMENT__, __BIGGEST_ALIGNMENT__> AlignedType;

答案 1 :(得分:1)

保罗·拉斯卡(Paul Laska)在altdevblogaday上写了一篇很棒的文章。以下是第一篇文章的链接:http://altdevblogaday.org/2011/04/11/ready-set-allocate-part-1/

在文章中,他关注块大小分配和对齐问题。它应该提供一个经过深思熟虑并且编写良好的解决方案来处理您的解除分配问题。

答案 2 :(得分:0)

有关在块的开头存储值的信息,请参阅my answer here。您可以根据需要稍微修改它。

答案 3 :(得分:0)

我所知道的对象大小跟踪的两种主要方法隐含在一个大小隔离的分配器中,边缘有元数据(例如Kingsley式分配器),或者将对象前面的大小固定为对象头(例如dlmalloc)。一个非常糟糕的第三个解决方案是维护每个已分配对象及其大小的映射。当然,该地图将由另一个分配器管理。

我认为你在正确的轨道上,并且你了解对齐方面的考虑是件好事。我试图在mt_alloc上查找一些信息,看看是否有其他选择或意外,但这些信息似乎并不容易。一些分配器有一种方法来查询对象大小(可能或可能不便宜)。如果deallocate函数需要明确地传递大小,那么我猜不会存在这样的函数,但你永远不知道。

如果对齐非常重要,那么您的计算需要稍微调整一下,因为分配器可能不会为您返回适当的内存。如果您对返回指针的对齐一无所知,则需要以下内容:

struct object_header {
    size_t size;
};

void * buf = xxmalloc (2 * alignment + size + sizeof(object_header));
void * alignedPtr = (void *) (((size_t) buf + sizeof(object_header) + alignment - 1) & ~(alignment - 1));

如果mt_alloc不能容忍释放内部指针上的对象,那么这个方案会给你带来一个问题,因为通过填充额外的空间进行对齐,你不再知道返回给你的原始地址。在这种情况下,您可能需要在标题中存储一个额外的字段。

取决于mt_alloc如何在内部管理内存,添加额外的标头也可以为您带来可观的开销。在一个按大小分隔的分配器中,在此标头上添加最多可以为页面大小的对象提供高达2倍的空间开销,此时您可以支付每个对象额外页面的成本。在其他分配器中,这可能不是问题,但需要注意的事项。