为什么使用malloc分配的虚拟内存没有被释放?

4

考虑以下这个 C 程序,它打印了私有虚拟内存:

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

#define _PSTAT64 
#include <sys/param.h> 
#include <sys/pstat.h> 
#include <sys/unistd.h> 

void pstatvm() 
{ 
    struct pst_vm_status pst; 
    int idx, count; 
    long long shared_vm = 0; 
    long long shared_ram = 0; 
    long long private_vm = 0; 
    long long private_ram = 0; 

    pid_t  pid = getpid();
    idx=0; 
    count = pstat_getprocvm(&pst, sizeof(pst), (size_t)pid, idx); 
    while (count > 0) { 
    switch ((long)pst.pst_type) { 
        case PS_IO: break; 
                        /* Don't count IO space. It really is not RAM or swap. */ 
        default: 
            if (pst.pst_flags & PS_SHARED) { 
                shared_vm += (long long) pst.pst_length; 
                shared_ram += (long long)pst.pst_phys_pages; 
            } else { 
                private_vm += (long long) pst.pst_length; 
                private_ram += (long long)pst.pst_phys_pages; 
            } 
                    break; 
    } 

    idx++; 
    count = pstat_getprocvm(&pst, sizeof(pst), (size_t)pid, idx); 
    } 
    printf("%d\t\t", pid); 
    printf("%lldK\t\t", shared_vm*4); 
    printf("%lldK\t\t", shared_ram*4); 
    printf("%lldK\t\t", private_vm*4); 
    printf("%lldK\n", private_ram*4); 
} 



int main()
{
    void *p=NULL;
    int cont = 1;

    printf("pid\t\tshared_vm\tshared_ram\tprivate_vm\tprivate_ram\n"); 

     while(cont < 100)
    {
        p = malloc((cont*10)*1024*1024);
        if (p == NULL)
        {
            printf("exit\n");
            exit(1);
        }
        pstatvm();
        free(p); p=NULL;
        sleep(1);
        cont += 10;
    }

    printf ("\n\n");
    cont = 0;
    while(cont < 10)
    {
        sleep(100);
        pstatvm();
        ++cont;
    }

}

为什么操作系统没有释放 private_vm?
pid             shared_vm       shared_ram      private_vm      private_ram
8988            3436K           2432K           26880K          320K
8988            3436K           2432K           129280K         336K
8988            3436K           2432K           231680K         352K
8988            3436K           2432K           334080K         368K
8988            3436K           2432K           436480K         384K
8988            3436K           2432K           538880K         400K
8988            3436K           2432K           641280K         416K
8988            3436K           2432K           743680K         432K
8988            3436K           2432K           846080K         448K
8988            3436K           2432K           948480K         464K


8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K
8988            3436K           2432K           948480K         464K

来自http://www.linuxforums.org/forum/programming-scripting/153369-solved-where-does-memory-allocated-malloc-actually-come.html:

在Linux / Unix中,所有应用程序内存都是虚拟的,除非被换出到磁盘,否则它指向物理内存中的某个位置。每个页面都可以单独寻址,因此,尽管您的应用程序堆(使用malloc()分配的内存)对应用程序而言似乎是连续的,但它实际上可能分散在整个物理RAM中


1
“除非它被交换到磁盘上,否则它将指向物理内存中的某个位置” - 这并不完全正确。它也可以取消映射到任何地方。 - Oliver Charlesworth
Jose,你能否发布一下你的主循环(while)中系统调用的日志,就像在Linux中通过strace或在FreeBSD中通过truss(在HP-UX中是tusc http://knowledgebase.progress.com/articles/Article/3669)打印的那样?这个大小的malloc可能会使用brk/sbrk来进行真正的堆分配,也可能会使用mmap;相比于碎片化的堆,mmap更容易取消映射(你的libc实现是什么?)。你能否将`pstatvm();`从内部while循环移动到循环后的位置(printf/pstat可能有它们自己的malloc调用,这会阻止堆空间的重用/碎片化)?HP-UX和libc的版本是多少? - osgx
不知道使用的函数/类型:似乎有太多的转换。永远不要使用不必要的转换。首先进行编译,然后如果你真的知道你在做什么,再加上转换。 - too honest for this site
2个回答

4
如果您想要分配大块内存并且确保在应用程序不再需要时它们真正释放,那么请直接使用POSIX的和函数,而不是依赖于足够大的请求被转换为的行为,虽然这可能是GNU/Linux上GNU C Library的工作方式,但它不是标准定义的要求,因此这种期望是不可移植的。
显然,您的HP-UX系统的会保留即使是大内存分配的基础存储。然而,请注意虚拟内存大小的增量只有约100 MB。这表明分配器正在重新使用先前释放的内存,避免了碎片化。也就是说,当您释放300MB块,并然后分配400MB的块时,它不是保留原先的300MB并然后分配一个全新的400MB块;它意识到以前释放的300MB空间可以被扩展。分配器中可能有其他隐藏的策略,你的程序的分配模式并没有展示出来。我们从您的程序中所知道的是:当我们进行单个大块的分配并释放它时,分配器能够将其保留并扩展以满足更大的分配请求。这并不能证明分配器始终会保留已释放的大内存分配,这可能仅是为了支持重复的而进行的优化。

3

malloc()free()不一定分配/释放虚拟内存,它们处理堆。虽然malloc()在需要时使用sbrk()来扩大堆,但是free()并不会收缩堆,因此分配的虚拟内存量与malloc()之后立即保持不变。


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