如何跟踪malloc和free?

4

可能是重复问题:
跟踪内存的简单C实现?

我需要知道在C程序中到目前为止使用了多少内存,以下是伪代码

#include <stdio.h>

int usedMemory =0;

void *MyMalloc(int size){
 usedMemory = usedMemory +size ;
 return malloc(size);
}

void MyFree(void *pointer){
/*****************what should i write here????*************/
}
int main(int argc, char *argv[])
{
    char *temp1= (char *)MyMalloc(100);
    char *temp2= (char *)MyMalloc(100);

    /*......other operations.........*/

    MyFree(temp1);
    MyFree(temp2);

    return 0;
}

有人能告诉我在 MyFree 方法中应该写什么吗?它用于减少从 usedMemory 中释放的内存量。


我想问为什么你想要实现这个?是为了自我测试还是为了分析数据使用情况?如果你只是想跟踪和了解内存使用情况,可以看看免费提供的 valgrind 应用程序。 - Grambot
1
如果malloc失败怎么办?试试这个:void *MyMalloc(int size) { void *tmp = malloc(size); if (tmp) usedMemory += size; return tmp; } :) - pmg
移除了 C++ 标签,因为问题特别涉及到 C - Nawaz
@Nawaz:不一定;OP强制转换了空指针,表明这是C ++。 - Kerrek SB
1
@Kerrek:或者OP有一个bug(缺少<stdlib.h>,因此不正确地使用了malloc),并使用强制转换来关闭编译器(并保留bug)。如果是C ++,则包含的头文件将是<iostrem><cstdio>或其他没有尾随.h的东西 :-) - pmg
5个回答

11

您可以多分配一些额外的字节,并将其大小存储在额外的字节中,这样您稍后就可以在MyFree函数中以很少的计算知道大小:

unsigned long int usedMemory = 0;

void *MyMalloc(int size)
{
  char *buffer = (char *) malloc(size + sizeof(int)); //allocate sizeof(int) extra bytes 
  if ( buffer == NULL) 
      return NULL; // no memory! 

  usedMemory += size ;      
  int *sizeBox = (int*)buffer;
  *sizeBox = size; //store the size in first sizeof(int) bytes!
  return buffer + sizeof(int); //return buffer after sizeof(int) bytes!
}

void MyFree(void *pointer)
{
   if (pointer == NULL)
       return; //no free

   char *buffer = (char*)pointer - sizeof(int); //get the start of the buffer
   int *sizeBox = (int*)buffer;
   usedMemory -= *sizeBox;
   free(buffer);
}

3
请注意,如果malloc返回的块比sizeof(int)对齐更高,则会返回非对齐内存,并且intsize_t之间的大小关系是可以不同的。对于特定平台上的快速解决方案,只需考虑使用合适的整数类型,当然可能是int,但需要注意以上内容。 - Steve Jessop
正如Steve所说 - 最好确定最大对齐方式,再多分配一些内存,然后在那个额外的部分开头写入int。 - Kerrek SB
@SteveJessop:老实说,我对齐方式不是很了解,所以在这方面无法做得更好(我不太自信)。请随意编辑此答案或发布新答案,以便我也能学习对齐问题。 - Nawaz
在可移植性方面,检测对齐要求是困难的。在 C++11 和 C1X 中,您应该将指针移动 sizeof(int)alignof(max_align_t) 中较大的那个,以便为 int 腾出空间而不会使分配脱离对齐。在 C99 中,您可以使用类似 sizeof(union { long double a; intmax_t b; void *c; }) 的东西来相对安全地操作,但这可能会浪费空间。 - Steve Jessop
能否分配一个结构体{int a; void* b},然后比较a和b的地址?这个差异应该给出这些可能对齐的块之间的确切距离,不是吗?(假设这个差异每次都相同...) - xmoex
1
@xmoex:说得好,如果你这样做 struct GetAlignment {char c; T t;};,那么 offsetof(struct GetAlignment, t) 就保证是类型 T 的对齐要求的倍数。在实践中,除非实现在结构体中添加无意义的多余填充,否则它将等于它。因此,如果您将认为可能具有大对齐要求的所有内容都放入一个联合中,称之为 T,那么您将获得最差的对齐要求,前提是实现没有定义任何您不知道的其他类型 - Steve Jessop

2
在C++中,您可以保留一个全局的std::map<void*, std::size_t>来跟踪每个分配块的大小;当分配时,您自己的分配器函数将注册大小,而释放函数将删除条目。(更新:或者像链接的问题建议的那样分配更多的内存并保存大小。)
更根本的问题是,在典型的C++程序中,这可能只有非常有限的用处:在那里进行分配主要有两种方式:1)通过显式的new表达式,调用::operator new(),后者(通常)调用malloc(),2)通过std::allocator<T>::allocate(),在许多平台上是基于::operator new()实现的。
问题在于您无法控制平台的具体细节。您可以替换全局operator-new以使用自己的MyMalloc(),但默认的std::allocator可能直接使用malloc(),因此不会受到影响。
对于调试目的,更清晰的方法是使用像valgrind这样的外部工具来跟踪堆使用情况。对于永久的内部使用,跟踪分配大小也会导致显著的性能损失。

2
您可以分配内存并将其大小存储在分配的块中(为简洁起见省略错误检查):
unsigned int totalAlloc = 0;

void *MyAlloc(unsigned int size)
{
    void *p;
    totalAlloc += size;

    p = malloc(size + sizeof(int));
    *(int *) p = size;
    return (void *)(((int *) p) + 1)
}

void MyFree(void *ptr)
{
    ptr = (void *)(((int *) ptr) -1 );
    totalAlloc -= * (int *) ptr;
    free(ptr);
}

这段代码实际上会预留比请求的内存更多的空间,以便在(通常)前四个字节中存储块的大小信息。稍后释放内存时可以检索到该信息。


2
问题在于您不再返回对齐良好的指针,这原则上会导致整个程序出现未定义行为。 - Kerrek SB
我不理解。这原则上不应该只是“多分配4个字节”吗?如果我选择分配一个char数组,并且按照原则从索引5开始向该数组写入数据,那么内存仍然被正确地分配了,不是吗? - Linus Kleen
已经分配了内存,但没有正确对齐。 - Kerrek SB
1
@Linus:假设您的C实现对某些内置类型T需要8字节对齐(这很少见,但标准允许--T可能是“long long”或“double”)。然后,malloc将返回一个8字节对齐的地址。您将指针加4,因此返回的地址不是8字节对齐的。因此,如果调用者执行T *buf = MyAlloc(sizeof(T)); if (buf) {*buf = 0;},则会出现未定义的行为。 - Steve Jessop

0
你需要管理一个指针+大小的malloc()列表。然后你可以在列表中搜索大小,并在free()中将其减少。
例如,查看以下示例中的操作方式: http://developers.sun.com/solaris/articles/lib_interposers_code.html#malloc_interposer.c 你可能有其他跟踪内存的可能性,例如: (顺便说一句,请接受一些回答吧!)

0

你可以尝试这样做……我强烈建议仅在调试目的下使用此方法!

#define MAXMEMBLOCKS 10000

typedef struct {
    int size;
    void* ptr;
} memblock;

typedef struct {
    int totalSize;
    int current;
    memblock memblocks[MAXMEMBLOCKS];
} currentMemory;

currentMemory mem;

void *MyMalloc(int size) {
    if (mem.current < MAXMEMBLOCKS) {
        mem.current += size;
        mem.memblocks[mem.current].size = size;
        mem.memblocks[mem.current].ptr = malloc(size);
        return mem.memblocks[mem.current++].ptr;
    } else {
        // you needed more memblocks than estimated
        return NULL;
    }
};

int MyFree(void *pointer) {
    int i;
    for (i = 0; i < mem.current; i++) {
        if (mem.memblocks[i].ptr == pointer) {
            mem.totalSize -= mem.memblocks[i].size;
            free(mem.memblocks[i].ptr);
            mem.current--;
            return 0;
        }
    }
    // you tried to free a block wich hasn't been allocated through MyMalloc()
    return -1;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接