2013-05-28 125 views
8

考虑下面的C代码,它创建10万4KB大小的页面,然后释放99,999页,最后释放最后一页:内存泄漏使用malloc

#include <stdio.h> 
#include <stdlib.h> 

#define NUM_PAGES 100000 

int main() { 
    void *pages[NUM_PAGES]; 

    int i; 
    for(i=0; i<NUM_PAGES; i++) { 
     pages[i] = malloc(4096); 
    } 

    printf("%d pages allocated.\n", NUM_PAGES); 
    getchar(); 

    for(i=0; i<NUM_PAGES-1; i++) { 
     free(pages[i]); 
    } 

    printf("%d pages freed.\n", NUM_PAGES-1); 
    getchar(); 

    free(pages[NUM_PAGES-1]); 

    printf("Last page freed.\n"); 
    getchar(); 

    return 0; 
} 

如果你编译,运行并且监视进程的内存使用情况,可以看到内存使用量在第一个getchar(内存分配为100,000页时)之前达到了大约400MB,然后即使在99,999页被解除分配后(第二个之后getchar),最后,当最后一页被解除分配时,它下降到1MB。

所以,我的问题是为什么会发生这种情况?为什么只有当所有页面被释放后,整个内存才会返回到操作系统?有没有任何页面大小或任何页面对齐,防止这种事情发生?我的意思是,当只有一个页面被释放时,是否有任何页面大小或对齐方式让任何mableced页面完全返回到操作系统?

+3

发生这种情况是因为C库使用'sbrk()'动态调整其未初始化数据的大小。它是顺序的,所以只有在最新的分配释放后才能缩小。如果将分配大小增加为131072字节(128k),'strace'表示它使用'mmap()'来代替分配,而每个'free()'实际上将分配返回给操作系统。因此,使用分配缓存,只向OS提出/返回更大的块。 –

+0

@NominalAnimal男人,你救了我的命!非常感谢上面和下面的评论。这正是我期待的答案。如果你已经回答了(没有评论),我会接受你的答案... – LuisABOL

回答

5

这完全依赖于实现,但我认为这与内存分配器的工作方式有关。通常情况下,当内存管理器需要更多内存时,它会调用sbrk函数来请求更多内存。这个函数的典型实现是操作系统存储一个指向内存中下一个空闲地址的指针,该进程可以获得空间。内存像堆栈一样增长,就像调用堆栈的工作方式一样。例如,如果您分配的内存五个页面,它可能是这样的:

(existing memory) | Page 0 | Page 1 | Page 2 | Page 3 | Page 4 | (next free spot) 

采用这种设置,如果你免费的网页0 - 4,里面的程序内存管理器将它们标记为免费,像这样的:

(existing memory) |         | Page 4 | (next free spot) 

由于OS被以堆叠状的方式分配存储器,直到第4页完成正在使用它不能回收从程序中的所有该内存。一旦你释放的最后页,进程的内存将是这样的:

(existing memory) |            (next free spot) 

而在这一点上,程序的内存管理器可以是巨大的自由空间量恢复到OS:

(existing memory) | (next free spot) 

换句话说,因为内存是作为一个堆栈分配的,所以直到你释放你分配的最后一件东西,操作系统不能回收任何内存。

希望这会有所帮助!

+0

好的,非常感谢你的回答!关于内存分配的很好的解释。但是,我想知道您是否知道避免堆栈式内存分配的任何方式,我的意思是,分配单独解除分配的各个页面。 – LuisABOL

+2

@ LuisAntonioBotelhoO.Leite您可能需要依赖于操作系统的调用来直接从操作系统获取/释放内存。在Windows下使用[VirtualAlloc](http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v = vs.85).aspx)和[VirtualFree](http:// msdn。微软。COM/EN-US /库/窗/桌面/ aa366892(V = vs.85)的.aspx)。在unices上,使用[sbrk(2)](http://linux.die.net/man/2/sbrk)。请注意,您被警告只使用malloc/free并避免使用sbrk。 – Anthony

+0

@ anthony-arnold非常感谢! – LuisABOL