malloc/calloc/free是库函数,在底层使用系统调用进行内存申请,自己添加了中间层进行管理,brk,sbrk,mmap,munmap是系统调用。申请的是虚存。
mmap 映射匿名页, 当发生缺页异常时, linux 内核为缺页分配一个新物理页,并将该物理页清 0。
对空闲的小内存块只会在 malloc 和 free 的时候进行合并。
主分配区与非主分配区用环形链表进行管理。 每一个分配区利用互斥锁( mutex)使线程对于该分配区的访问互斥。
每个进程只有一个主分配区,但可能存在多个非主分配区, 主分配区可以访问进程的 heap 区域和 mmap 映射区域, 非主分配区每次使用 mmap()向操作系统“批发” HEAP_MAX_SIZE
当某一线程需要调用 malloc()分配内存空间时, 该线程先查看线程私有变量中是否已经存在一个分配区,如果存在, 尝试对该分配区加锁,如果加锁成功,使用该分配区分配内存,如果失败, 该线程搜索循环链表试图获得一个没有加锁的分配区。如果所有的分配区都已经加锁,那么 malloc()会开辟一个新的分配区,把该分配区加入到全局分配区循环链表并加锁,然后使用该分配区进行分配内存操作。在释放操作中,线程同样试图获得待释放内存块所在分配区的锁,如果该分配区正在被别的线程使用,则需要等待直到其他线程释放该分配区的互斥锁之后才可以进行释放操作
Bins
ptmalloc将相似大小的 chunk 用双向链表链接起来,这样的一个链表被称为一个 bin。 Ptmalloc 一共维护了 128 个 bin,并使用一个数组来存储这些 bin。数组中的第一个为 unsorted bin, 数组中从 2 开始编号的前 64 个 bin 称为 small bins,同一个 small bin中的 chunk具有相同的大小。两个相邻的 small bin中的 chunk大小相差 8bytes。small bins 中的 chunk 按照最近使用顺序进行排列,最后释放的 chunk 被链接到链表的头部,而申请 chunk 是从链表尾部开始。large bins 中的每一个 bin 分别包含了一个给定范围内的 chunk,其中的 chunk 按大小序排列。相同大小的 chunk 同样按照最近使用顺序排列。ptmalloc 使用“ smallest-first, best-fit”原则在空闲 large bins 中查找合适的 chunk
当空闲的 chunk 被链接到 bin 中的时候, ptmalloc 会把表示该 chunk 是否处于使用中的标志 P 设为 0( 注意, 这个标志实际上处在下一个 chunk 中), 同时 ptmalloc 还会检查它前后的 chunk 是否也是空闲的, 如果是的话, ptmalloc 会首先把它们合并为一个大的 chunk,然后将合并后的 chunk 放到 unstored bin 中。 要注意的是, 并不是所有的 chunk 被释放后就立即被放到 bin 中。 ptmalloc 为了提高分配的速度, 会把一些小的的 chunk 先放到一个叫做fast bins 的容器内。
fast_bins
ptmalloc 中在分配过程中引入了 fast bins,不大于 max_fast(默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins中, fast bins 中的 chunk 并不改变它的使用标志 P。 这样也就无法将它们合并, 当需要给用户分配的 chunk 小于或等于 max_fast 时, ptmalloc 首先会在 fast bins 中查找相应的空闲块,然后才会去查找 bins中的空闲 chunk。在某个特定的时候,ptmalloc会遍历 fast bins中的 chunk,将相邻的空闲 chunk 进行合并, 并将合并后的 chunk 加入 unsorted bin 中
unstorted_bin
被用户释放的chunk大于max_fast,或者fast bins中的空闲chunk合并后, 这些chunk首先会被放到unsorted bin队列中, 在进行malloc操作的时候,如果在fast bins中没有找到合适的chunk,则ptmalloc会先在unsorted_bin中查找合适的空闲chunk, 然后才查找bins。 如果unsorted bin不能满足分配要求。malloc便会将unsorted bin中的chunk加入bins中.
top_bin
内存是按地址从低向高进行分配的, 在空闲内存的最高处, 必然存在着一块空闲 chunk, 叫做 top chunk。 当 bins 和 fast bins 都不能满足分配需要的时候,ptmalloc 会设法在 top chunk 中分出一块内存给用户
mmaped chunk
当需要分配的chunk足够大, 而且fast bins和bins都不能满足要求, 甚至top chunk本身也不能满足分配需求时,ptmalloc会使用mmap来直接使用内存映射来将页映射到进程空间。 这样分配的chunk在被free时将直接解除映射, 于是就将内存归还给了操作系统
初始化
在使 malloc 之前,brk的值等于 start_brk,也就是说 heap大小为 0。ptmalloc在开始时,若请求的空间小于 mmap分配阈值( mmap threshold,默认值为 128KB)时,主分配区会调用 sbrk()增加一块大小为 (128KB + chunk_size) align 4KB 的空间作为 heap。 非主分配区会调用 mmap 映射一块大小为HEAP_MAX_SIZE ( 32 位系统上默认为 1MB,64 位系统上默认为 64MB)的空间作为 sub-heap。
分配过程
ptmalloc 首先会查找 fast bins,如果不能找到匹配的 chunk, 则查找 small bins。 若还是不行,合并 fast bins,把 chunk加入 unsorted bin,在 unsorted bin 中查找, 若还是不行, 把 unsorted bin 中的 chunk 全加入 large bins 中,并查找 large bins。在 fast bins 和 small bins 中的查找都需要精确匹配,而在 large bins 中查找时, 则遵循“ smallest-first, best-fit”的原则, 不需要精确匹配。若以上方法都失败了, 则 ptmalloc 会考虑使用 top chunk。 若 top chunk 也不能满足分配要求。 而且所需 chunk 大小大于 mmap 分配阈值, 则使用 mmap 进行分配。 否则增加heap, 增大 top chunk
参数值
M_MXFAST 用于设置 fast bins 中保存的 chunk 的最大大小,默认值为 64B, fast bins 中保存的 chunk 在一段时间内不会被合并, 分配小对象时可以首先查找 fast bins
M_TRIM_THRESHOLD 用于设置 mmap 收缩阈值,默认值为 128KB
M_MMAP_THRESHOLD 用于设置 mmap 分配阈值,默认值为 128K
Large bins 用于存储大于等于 512B 或 1024B 的空闲 chunk,这些 chunk 使用双向链表的形式按大小顺序排序,分配内存时按最近匹配方式从 large bins 中分配 chunk。
Fast bins 是小内存块的高速缓存
Unsorted bin 可以看作是 small bins 和 large bins 的 cache,
//linux源码中的两种内存申请
malloc / vmalloc,分配的虚拟地址是连续的,物理地址无需连续
kmalloc 分配的虚拟地址连续,物理地址也是连续的,通过虚拟地址+0xc0000000来进行偏移
slab层,相当于内存池,不用的内存块在其中进行管理,需要就从中拿 kmallo接口建立在slab层之上
每种对象建立一个slab 避免频繁的分配和释放页
slab分配器接口
kmem_cache_create 创建高速缓存
kmem_cache_alloc 从告诉缓存中取对象 kmem_cache_free 释放该对象到高速缓存中去
kmem_cache_destroy销毁
//动态内存管理的常规方法
边界表示法:
将系统中的链表分为大小不同的块进行区别然后连成链表,然后进行申请,
释放的时候三种情况:1>左右皆被使用 2>左边或者右边一处空闲一处使用 3>左右皆空闲
伙伴系统:
申请空间n,则申请的大小在2^(m-1) < n < 2^m,申请的空间是2的m幂次方的空间。
一个管理链表,初始化后,申请一个m,首先进行大小计算,然后在该大小的下标下找是否有空闲块,有的话就申请成功,删除该链表中的该块。否则,就寻找大的有空闲块的进行分割,拿走自己所需要的,将其余的按照大小链入链表。
释放,首先看自己的伙伴是否空闲,空闲的话合并,放入大小对等的链表中,然后再进行判断伙伴的空闲与否,直至不能合并。
内部碎片多,算法简单,速度快。