前言

    堆内存是linux进程空间中一片可以动态扩展或者伸缩的内存区域,一般位于bss之后。


阅读《嵌入式C语言自我修养》笔记


    主要有以下几个函数:

#include<stdio.h>
void *malloc(int size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr,size_t size);   /*可以调整内存的大小*/

    对于着四个函数的用法,可以百度。

一、堆内存管理

    一般在裸机环境下,在经过多次申请和释放堆内存后,会有越来越多的碎片内存,致使后续申请失败,所以在裸机环境中,很少见到使用堆内存的。绝大多数还是通过操作系统进行管理。

二、linux堆内存管理

1.mm_struct

    对于用户申请小内存块,会在bss段的后面批准一块内存给内存使用;当大于128KB时,则需要通过mmap系统调用,映射一片内存给用户使用,映射区域在用户进程栈附近。
    在每一个linux用户进程中,linux内核都会使用一个task_struct 结构体描述,其中,有mm_struct结构体,用来描述该进程的代码段,数据段,堆栈的起始地址。

struct mm_struct{
....
unsigned long mmap_base;
unsigned long start_code,end_code,start_data,end_data;
unsigned long start_brk,brk,start_stack;
unsigned long arg_start,arg_end,env_start,env_end;

mm_context_t contest;
...
}

    start_brk 是便是堆区的起始地址,brk则是结束边界地址。当用户malloc()申请堆区大小大于当前堆区时,malloc()通过brk()系统调用,修改brk的地址,来改变大小。

2.内存分配器

    为了减小系统调用的次数,提高效率,可以在用户层面对堆内存介入管理。

    内存分配器通过系统调用brk()/mmap()向linux内存管理子系统“批发”内存,同时实现了malloc()/free(),供用户使用。
    当用户使用free()释放内存时,释放的内存并不立即返回给内核,而是通过内存分配器缓存在用户空间,通过链表收集起来,等下次用户在申请时,查找合适大小的,如果没有,再去brk()系统调用。

在这里插入图片描述

3.ptmalloc2

深入理解 glibc malloc:内存分配器实现原理
Glibc内存管理-ptmalloc2
内存管理:malloc的bin和特殊chunk

    Linux下的c标准库glibc使用ptmalloc2作为内存分配器。对于用户申请的每一个内存块都叫成chunk:

struct malloc_trunk
{
	INTERNAL_SIZE_T mchunk_prev_size;
	INTERNAL_SIZE_T  mchunk_size;
	struct malloc_chunk* fd;
	struct malloc_chunk* bk;
	struct malloc_chunk* fd_nextsize;
	struct malloc_chunk* bk_nextsize;	
}

    用户释放掉的内存并不是马上就归还给操作系统,ptmalloc会统一管理heap和mmap映射区中的空闲的chunk,当用户进行下一次请求分配时,ptmalloc会试图从空闲的内存中挑选一块给用户,这样可以避免频繁的系统调用,降低了内存​分配的开销。ptmalloc将大小相似的chunk用双向循环链表连接起来,这样的一个链表称为bin。ptmalloc中一共维护了128个bin,并使用一个数组来存储这些bin(数组实际存储的是指针)。

在这里插入图片描述
    用户释放掉的内存块不会立即放到bins中,而是先放到unsorted bin 上,等下次申请会先在中查看有没有合适的。在free small bin和large bin后,free chunk会被放入unsorted bin中,这样可以加快内存的分配和释放操作,因为不需要花费额外的时间去查找合适的bin了

分类数组索引内存块大小步长个数
unsorted bin--1
small bins[2,63][16,504]862
small bins[64,94][512,2488]6431
small bins[95,111][2496,10744]51217
small bins[112,120][10752,45048]40969
small bins[121,123][45056,163832]327683
small bins[124,126][163840,786424]2621443
small bins[127][786432,2^64]-1

    还有一些bins,如fast bin,用户释放掉的内存块小于M_NAXFAST(32位默认64字节)会首先放到fast bin.
总的讲:

当用户申请一块内存时,内存分配器就根据申请内存的大小从bins查找合适的内存块。当然这些零碎的内存块也会在合适的情况下合并。

当用户申请大于128KB内存,则需要mmap.
Linux内存分配小结–malloc、brk、mmap


Logo