glibc内存分配释放示例

23 篇文章 0 订阅

glibc中的内存管理采用的是ptmalloc机制其malloc和free函数操作的是进程虚拟地址空间。

malloc分配内存是通过brk(sbrk)mmap这两个系统调用实现的,brk(sbrk)通过移动进程地址空间的heap顶部指针向地址增大/缩小方向移动,mmap是在进程地址空间的heap和stack中间分配虚拟地址空间。

不管使用的是哪种内存分配方式,仅仅都是操作虚拟地址空间,并没有实际物理内存的分配,只有当进程需要访问该虚拟地址空间时,发生缺页中断才会实际分配物理内存并建立映射关系。

对于内存释放free而言,由brk(sbrk)分配的内存并不一定会真正释放,默认情况下,只有当heap最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,才会执行内存紧缩操作(trim)释放内存。即使没有真正释放,当应用程序再次请求内存并且之前free的内存大小满足请求条件时可以直接返回给应用程序再次使用。

mmap分配的内存在free时会真正释放。

具体实现可以参看https://blog.csdn.net/just_lion/article/details/80592027

以下是示例程序:

一,sbrk内存分配模式,(单次分配<128k)

1)分配2048个64k大小的内存

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

#define chunk_size 64*1024
#define chunk_num 2048

void* chunk_array[chunk_num];

int main(void)
{
    int i,j;

    for (i=0; i<chunk_num; i++)
    {
        chunk_array[i] = malloc(chunk_size);
        for(j =0; j<chunk_size; j++)
        {
            *((char*)chunk_array[i] + j) = '\0';
        }
    }

    sleep(20000);

    return 0;
}

此时通过top -p <pid>查看内存占用为:

其中VIRT为虚拟内存占用, RES为物理内存占用。

2) 从heap底部(低地址)执行部分free

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

#define chunk_size 64*1024
#define chunk_num 2048

void* chunk_array[chunk_num];

int main(void)
{
    int i,j;

    for (i=0; i<chunk_num; i++)
    {
        chunk_array[i] = malloc(chunk_size);
        for(j =0; j<chunk_size; j++)
        {
            *((char*)chunk_array[i] + j) = '\0';
        }
    }

    for (i=0; i<chunk_num-10; i++)
    {
        free(chunk_array[i]);
    }

    sleep(20000);

    return 0;
}

 此时查看内存占用,没有任何内存释放,因为heap顶部没有释放。

3) 从heap顶部(高地址)执行部分free

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

#define chunk_size 64*1024
#define chunk_num 2048

void* chunk_array[chunk_num];

int main(void)
{
    int i,j;

    for (i=0; i<chunk_num; i++)
    {
        chunk_array[i] = malloc(chunk_size);
        for(j =0; j<chunk_size; j++)
        {
            *((char*)chunk_array[i] + j) = '\0';
        }
    }

    for (i=chunk_num-1; i>10; i--)
    {
        free(chunk_array[i]);
    }

    sleep(20000);

    return 0;
}

查看内存占用:

此时内存基本释放完毕。

 

二,mmap内存分配模式,(单次分配>=128k)

1) 分配2048个128k大小的内存

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

#define chunk_size 128*1024
#define chunk_num 2048

void* chunk_array[chunk_num];

int main(void)
{
    int i,j;

    for (i=0; i<chunk_num; i++)
    {
        chunk_array[i] = malloc(chunk_size);
        for(j =0; j<chunk_size; j++)
        {
            *((char*)chunk_array[i] + j) = '\0';
        }
    }

    sleep(20000);

    return 0;
}

此时内存占用为:

2) 从低地址执行部分free

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

#define chunk_size 128*1024
#define chunk_num 2048

void* chunk_array[chunk_num];

int main(void)
{
    int i,j;

    for (i=0; i<chunk_num; i++)
    {
        chunk_array[i] = malloc(chunk_size);
        for(j =0; j<chunk_size; j++)
        {
            *((char*)chunk_array[i] + j) = '\0';
        }
    }

    for (i=0; i<chunk_num-10; i++)
    {
        free(chunk_array[i]);
    }

    sleep(20000);

    return 0;
}

此时查看内存占用:

可见内存基本释放完毕。

 

glibc内存分配器在分配内存时遵循一定的规则,以便提高内存分配的效率和性能。下面是glibc内存分配器的一些规则: 1. 内存分配器使用多个arena。每个arena包含多个内存块,用于存储分配的内存。通过使用多个arena,可以避免不同线程之间的竞争,从而提高内存分配的效率。 2. 内存分配器使用多个free list。每个free list包含一组相同大小的空闲内存块,用于存储已经释放内存。通过使用多个free list,可以避免不同大小的内存块之间的碎片,从而提高内存分配的效率。 3. 内存分配器会尽量复用已经释放内存块。当需要分配内存时,内存分配器会首先查找free list中是否有相同大小的空闲内存块,如果有,则直接返回该内存块的地址;否则,会尝试从arena中分配新的内存块。 4. 内存分配器会尝试合并相邻的空闲内存块。当一个内存块被释放后,内存分配器会检查其相邻的空闲内存块,如果存在,则会将它们合并成一个更大的内存块,以避免碎片的产生。 5. 内存分配器会使用mmap系统调用来分配大块的内存。当需要分配大块的内存时,内存分配器会使用mmap系统调用来分配内存。这种方式可以避免内存碎片的产生,但是会增加系统调用的开销。 需要注意的是,不同的内存分配器具有不同的特点和优缺点,需要根据具体的应用场景来选择合适的内存分配器。同时,内存的分配和释放需要谨慎使用,否则容易出现内存泄漏等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值